Compare commits

..

1039 Commits

Author SHA1 Message Date
Applevangelist
ac7cdb7d16 Merge remote-tracking branch 'origin/develop' into branch 2025-05-24 14:31:55 +02:00
Applevangelist
ffc5cff275 Merge remote-tracking branch 'origin/master' into branch 2025-05-24 14:31:27 +02:00
Thomas
432fc0ef4b Merge pull request #2307 from shaji-Dev/develop
[ADDED] `TARGET:GetObjects()` and `TARGET:GetCoordinates()` in case of SET
2025-05-24 12:50:44 +02:00
shaji
20c50c751f [ADDED] Sets target support for Auftrag STRIKE and BOMBING 2025-05-24 11:29:01 +02:00
shaji
d3b62d0260 [ADDED] TARGET:GetObjects() and TARGET:GetCoordinates() in case of SET 2025-05-24 11:18:41 +02:00
Applevangelist
d0449265c1 Merge remote-tracking branch 'origin/master' into develop 2025-05-21 10:22:16 +02:00
Applevangelist
a4feafab8e #POINT - improved IsDay() for Kola 2025-05-21 10:21:48 +02:00
Applevangelist
309eedd165 Merge remote-tracking branch 'origin/master' into develop 2025-05-21 10:05:30 +02:00
Applevangelist
997baf21a0 #CSAR fix for ADF beacons 2025-05-21 10:04:58 +02:00
Applevangelist
e456573eec #CSAR fix for ADF beacons 2025-05-21 10:04:50 +02:00
Applevangelist
daa2b7da4d xxx 2025-05-16 13:58:55 +02:00
Applevangelist
5351c4f093 xx 2025-05-16 13:43:39 +02:00
Applevangelist
fa676cc750 Merge remote-tracking branch 'origin/master' into develop 2025-05-16 13:43:27 +02:00
Applevangelist
b126cc00d0 xx 2025-05-16 13:43:03 +02:00
Applevangelist
1e0c27f599 Merge remote-tracking branch 'origin/master' into develop 2025-05-16 12:00:32 +02:00
Applevangelist
09b7922b84 Small fixes 2025-05-16 11:58:40 +02:00
Applevangelist
5ff314d8e0 Merge remote-tracking branch 'origin/master' into branch
# Conflicts:
#	Moose Development/Moose/AI/AI_A2A_Cap.lua
#	Moose Development/Moose/AI/AI_A2A_Dispatcher.lua
#	Moose Development/Moose/AI/AI_A2A_Gci.lua
#	Moose Development/Moose/AI/AI_A2A_Patrol.lua
#	Moose Development/Moose/AI/AI_A2G_BAI.lua
#	Moose Development/Moose/AI/AI_A2G_CAS.lua
#	Moose Development/Moose/AI/AI_A2G_Dispatcher.lua
#	Moose Development/Moose/AI/AI_A2G_SEAD.lua
#	Moose Development/Moose/AI/AI_Air.lua
#	Moose Development/Moose/AI/AI_Air_Dispatcher.lua
#	Moose Development/Moose/AI/AI_Air_Engage.lua
#	Moose Development/Moose/AI/AI_Air_Patrol.lua
#	Moose Development/Moose/AI/AI_Air_Squadron.lua
#	Moose Development/Moose/AI/AI_BAI.lua
#	Moose Development/Moose/AI/AI_Balancer.lua
#	Moose Development/Moose/AI/AI_CAP.lua
#	Moose Development/Moose/AI/AI_CAS.lua
#	Moose Development/Moose/AI/AI_Cargo.lua
#	Moose Development/Moose/AI/AI_Cargo_APC.lua
#	Moose Development/Moose/AI/AI_Cargo_Airplane.lua
#	Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua
#	Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua
#	Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua
#	Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua
#	Moose Development/Moose/AI/AI_Cargo_Dispatcher_Ship.lua
#	Moose Development/Moose/AI/AI_Cargo_Helicopter.lua
#	Moose Development/Moose/AI/AI_Cargo_Ship.lua
#	Moose Development/Moose/AI/AI_Escort.lua
#	Moose Development/Moose/AI/AI_Escort_Dispatcher.lua
#	Moose Development/Moose/AI/AI_Escort_Dispatcher_Request.lua
#	Moose Development/Moose/AI/AI_Escort_Request.lua
#	Moose Development/Moose/AI/AI_Formation.lua
#	Moose Development/Moose/AI/AI_Patrol.lua
#	Moose Development/Moose/Actions/Act_Account.lua
#	Moose Development/Moose/Actions/Act_Assign.lua
#	Moose Development/Moose/Actions/Act_Assist.lua
#	Moose Development/Moose/Actions/Act_Route.lua
#	Moose Development/Moose/Cargo/Cargo.lua
#	Moose Development/Moose/Cargo/CargoCrate.lua
#	Moose Development/Moose/Cargo/CargoGroup.lua
#	Moose Development/Moose/Cargo/CargoSlingload.lua
#	Moose Development/Moose/Cargo/CargoUnit.lua
#	Moose Development/Moose/Tasking/CommandCenter.lua
#	Moose Development/Moose/Tasking/DetectionManager.lua
#	Moose Development/Moose/Tasking/Mission.lua
#	Moose Development/Moose/Tasking/Task.lua
#	Moose Development/Moose/Tasking/TaskInfo.lua
#	Moose Development/Moose/Tasking/Task_A2A.lua
#	Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua
#	Moose Development/Moose/Tasking/Task_A2G.lua
#	Moose Development/Moose/Tasking/Task_A2G_Dispatcher.lua
#	Moose Development/Moose/Tasking/Task_CARGO.lua
#	Moose Development/Moose/Tasking/Task_Capture_Dispatcher.lua
#	Moose Development/Moose/Tasking/Task_Capture_Zone.lua
#	Moose Development/Moose/Tasking/Task_Cargo_CSAR.lua
#	Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua
#	Moose Development/Moose/Tasking/Task_Cargo_Transport.lua
#	Moose Development/Moose/Tasking/Task_Manager.lua
2025-05-16 11:56:23 +02:00
Applevangelist
45bb0fff8a xx 2025-05-16 11:48:14 +02:00
Applevangelist
9a10b34933 xxx 2025-05-15 18:24:45 +02:00
Applevangelist
66032d6894 #EASYGCICAP - added SetDefaultTakeOffType() 2025-05-15 18:24:26 +02:00
Applevangelist
7c98a793c7 Merge remote-tracking branch 'origin/master' into develop 2025-05-15 17:08:28 +02:00
Applevangelist
7a5b9a75f3 #AIRBASE - added Syria Marka AFB 2025-05-15 17:07:55 +02:00
Applevangelist
0ea5631955 Merge remote-tracking branch 'origin/master' into develop 2025-05-15 13:20:07 +02:00
Applevangelist
4bab2ee1de Add deprecated banner 2025-05-15 13:19:42 +02:00
Applevangelist
f5b9713639 Merge remote-tracking branch 'origin/master' into develop 2025-05-15 11:42:54 +02:00
Applevangelist
1bfb4fc4e1 Small fix 2025-05-15 11:42:27 +02:00
Applevangelist
d7defe6f7f xx 2025-05-15 11:42:05 +02:00
Applevangelist
1231541053 xx 2025-05-15 11:39:42 +02:00
Applevangelist
5e45e4a8ec xxx 2025-05-15 10:05:25 +02:00
Applevangelist
ebb94c07b3 Small fix 2025-05-15 10:05:11 +02:00
Applevangelist
5a96a6078a Merge remote-tracking branch 'origin/develop' into branch 2025-05-15 08:53:09 +02:00
Applevangelist
8fdeca99be Merge remote-tracking branch 'origin/master' into branch 2025-05-15 08:53:03 +02:00
Applevangelist
f4cdbec376 Merge remote-tracking branch 'origin/develop' into develop 2025-05-15 08:52:18 +02:00
Applevangelist
21d5a5dfac Merge remote-tracking branch 'origin/master' into develop 2025-05-15 08:52:14 +02:00
Applevangelist
f8947aab9c #MANTIS - Make DLINK caching (DEV version) configureable 2025-05-15 08:51:44 +02:00
Applevangelist
db869bcb6d #MANTIS - Make DLINK caching (DEV version) configureable 2025-05-15 08:51:30 +02:00
Applevangelist
6ff2725a1d #MANTIS - Make DLINK cache time configureable 2025-05-15 08:50:56 +02:00
Thomas
36a0cfd635 Merge pull request #2306 from FlightControl-Master/master
Merge
2025-05-15 06:52:23 +02:00
Thomas
ea4a1f9ff9 Merge pull request #2305 from shaji-Dev/master
[FIXED] Kola Airbase name "Alakourtti" to "Alakurtti"
2025-05-15 06:42:07 +02:00
shaji
20406e40ca [FIXED] Kola Airbase name "Alakourtti" to "Alakurtti" 2025-05-15 01:17:55 +02:00
Applevangelist
acbcb14cba Merge remote-tracking branch 'origin/master' into develop 2025-05-12 17:51:12 +02:00
Applevangelist
3b50fee5a0 #CTLD - extract troops, check for groupname in task properties of PLAYERTASKs, so the right people rescue the correct group 2025-05-12 17:50:37 +02:00
Applevangelist
804004198b #MANTIS - Update docu 2025-05-12 17:49:25 +02:00
Applevangelist
2b83d516d5 xxx 2025-05-12 17:48:50 +02:00
Applevangelist
c5ec17a473 Merge remote-tracking branch 'origin/develop' into branch
# Conflicts:
#	Moose Development/Moose/AI/AI_Formation.lua
2025-05-12 08:32:24 +02:00
Applevangelist
a81de5e23f Merge remote-tracking branch 'origin/master' into branch
# Conflicts:
#	Moose Development/Moose/AI/AI_Formation.lua
#	Moose Development/Moose/Actions/Act_Account.lua
#	Moose Development/Moose/Functional/Fox.lua
#	Moose Development/Moose/Tasking/Task_CARGO.lua
2025-05-12 08:31:17 +02:00
Applevangelist
c1855cefd3 CTLD 2025-05-12 08:30:35 +02:00
Thomas
67ef6c6e7b Merge pull request #2304 from FlightControl-Master/master
Nil check
2025-05-12 07:01:15 +02:00
Thomas
5b8b8a5566 Merge pull request #2303 from leka1986/master
Fix Line 77390: attempt to index local 'zonecoord' (a nil value)
2025-05-12 07:00:19 +02:00
leka1986
0468bacc0b Fix Line 77390: attempt to index local 'zonecoord' (a nil value) 2025-05-11 21:15:31 +02:00
Applevangelist
eacc43cb5a Merge remote-tracking branch 'origin/develop' into develop 2025-05-04 12:54:33 +02:00
Applevangelist
f3dcde075c Merge remote-tracking branch 'origin/master' into develop
# Conflicts:
#	Moose Development/Moose/AI/AI_Formation.lua
#	Moose Development/Moose/Functional/Fox.lua
2025-05-04 12:54:27 +02:00
Thomas
7eba1349ae Merge pull request #2301 from leka1986/master
VS Code pointed out some errors, like duplicated values in tables. This is first time I do changes through VS Code, Hope for the best
2025-05-04 10:47:28 +02:00
leka1986
b6074a4795 VS Code pointed out some errors, like duplicated values in tables. This is first time I do changes through VS Code, Hope for the best 2025-05-04 10:38:44 +02:00
Thomas
615afb7cc4 Merge pull request #2300 from FlightControl-Master/master
Merge from master
2025-05-03 20:33:49 +02:00
Thomas
36c9f551d9 Merge pull request #2299 from leka1986/patch-3
Update CTLD.lua
2025-05-03 20:32:40 +02:00
leka1986
89c3f7310b Update CTLD.lua
Changed the naming from Get only to Get

and the order is Get, Get and load
Instead of Get and load, and Get only.

Changed the order on Pack and load, Pack and remove, pack only to 
Pack, Pack and load, Pack and remove.

Same goes for Drop and build, Drop only to Drop, Drop and build.

It purely subjective what one would like, so I leave it up to you. If you like it this way or the first version, you decide, then disregard this change.
2025-05-03 19:22:59 +02:00
Applevangelist
e9d812f315 Merge remote-tracking branch 'origin/develop' into branch 2025-05-03 17:03:30 +02:00
Applevangelist
f95d27bf80 Merge remote-tracking branch 'origin/master' into branch 2025-05-03 17:03:14 +02:00
Applevangelist
6791a0e704 Merge remote-tracking branch 'origin/master' into develop 2025-05-03 17:02:29 +02:00
Applevangelist
a6b622ed31 #CTLD - Additional features by Lekaa to drop and build one/many in one go and pack/load or get/load in one go 2025-05-03 17:01:02 +02:00
Applevangelist
be98ee52b7 xxx 2025-05-03 16:59:07 +02:00
Frank
f1af3a50b8 Update Unit.lua
- GetAmmo HE shells can also be named "HESH"
2025-05-02 21:36:03 +02:00
Applevangelist
f999e45323 Merge remote-tracking branch 'origin/master' into develop 2025-05-02 10:53:29 +02:00
Applevangelist
0c90e90c18 #CSAR - fixed design issue that prevented usage of ZONE objects as MASHes 2025-05-02 10:53:02 +02:00
Applevangelist
4c9210586c #CSAR - fix design issue that prevented usage of ZONE objects as MASHes 2025-05-02 10:52:34 +02:00
Applevangelist
dc51f83249 Merge remote-tracking branch 'origin/develop' into branch 2025-05-02 09:35:22 +02:00
Applevangelist
23ed39a708 Merge remote-tracking branch 'origin/master' into branch 2025-05-02 09:35:14 +02:00
Applevangelist
44c59b97e8 CSAR 2025-05-02 09:34:43 +02:00
Frank
4747f3f48f Merge branch 'master' into develop 2025-05-01 22:11:54 +02:00
Frank
f97ef25104 Update Unit.lua 2025-05-01 22:11:43 +02:00
Thomas
e1a670185e Merge pull request #2298 from FlightControl-Master/master
CTLD - Fix issue when a ship zone is destroyed
2025-04-28 09:22:32 +02:00
Thomas
069c0aa03f Merge pull request #2297 from FlightControl-Master/Applevangelist-patch-1
Update CTLD.lua
2025-04-28 09:20:58 +02:00
Thomas
b145588ed5 Update CTLD.lua
Fix an issue when a ship is used as loading zone and the ship is destroyed
2025-04-28 09:20:04 +02:00
Applevangelist
9721f743a0 Merge remote-tracking branch 'origin/develop' into branch 2025-04-27 11:27:59 +02:00
Applevangelist
63899923fb Merge remote-tracking branch 'origin/master' into branch 2025-04-27 11:27:40 +02:00
Applevangelist
b678e2cde1 Merge remote-tracking branch 'origin/master' into develop 2025-04-27 11:26:51 +02:00
Applevangelist
db23a0bf2b #FlightGroup - added nil check for name 2025-04-27 11:26:22 +02:00
Applevangelist
3ad60a95ce #MANTIS - Adde Gepard data, made Roland Short 2025-04-27 11:25:46 +02:00
Applevangelist
4fbbf70b8e xx 2025-04-27 11:23:16 +02:00
Frank
5c3b7312c0 Update Range.lua
- Reduced smoke duration at impact coordinate to 30 seconds
2025-04-26 23:55:13 +02:00
Frank
f177d0a257 Merge branch 'master' into develop 2025-04-26 23:39:34 +02:00
Frank
ac4b620f16 COORDINATE
- Improved Smoke and Fire and Smoke functions by adding delay and duration parameters
2025-04-26 23:39:22 +02:00
Thomas
c49b56eefc Merge pull request #2296 from FlightControl-Master/master
Merge from master
2025-04-26 07:53:01 +02:00
Thomas
ccada18a6a Merge pull request #2295 from leka1986/patch-2
Update CTLD.lua
2025-04-26 07:52:01 +02:00
leka1986
1547d66327 Update CTLD.lua
Getting rid of this error,
bad argument #1 to 'find' (string expected, got nil)
2025-04-26 03:28:16 +02:00
Applevangelist
e38dc77c4b Merge remote-tracking branch 'origin/develop' into branch 2025-04-24 14:49:44 +02:00
Applevangelist
f283e68a63 Merge remote-tracking branch 'origin/master' into branch 2025-04-24 14:49:33 +02:00
Applevangelist
e21236655a Merge remote-tracking branch 'origin/develop' into develop 2025-04-24 14:48:17 +02:00
Applevangelist
cdad3bd058 #PLAYERTASK - make an existing marker move with the group if the group is moving around 2025-04-24 14:48:12 +02:00
Applevangelist
96d52ea393 xxx 2025-04-24 14:47:02 +02:00
Thomas
0db9c27f7e Merge pull request #2294 from FlightControl-Master/master
Merge from master
2025-04-23 14:23:04 +02:00
Thomas
8042e8bfaf Merge pull request #2293 from shaji-Dev/master
[ADDED] AIRBOSS:SetMaxSectionDistance
2025-04-23 14:21:31 +02:00
shaji
dd7b87e9cd [ADDED] AIRBOSS:SetMaxSectionDistance 2025-04-23 13:39:55 +02:00
Thomas
68a539923f Merge pull request #2292 from FlightControl-Master/master
Range os.date fix
2025-04-23 09:24:02 +02:00
Thomas
460d2768ff Merge pull request #2291 from FlightControl-Master/Applevangelist-patch-1
Update Range.lua
2025-04-23 09:03:54 +02:00
Thomas
3c74272749 Update Range.lua
#RANGE log an error if os/os.date() are not available
2025-04-23 09:00:05 +02:00
Thomas
3442725525 Merge pull request #2290 from FlightControl-Master/master
Merge from master
2025-04-23 06:29:32 +02:00
Thomas
82c409d77a Merge pull request #2289 from shaji-Dev/master
[ADDED] IsAlive condition for Unit and Group out message
2025-04-23 06:28:13 +02:00
shaji
195aac4504 [ADDED] IsAlive condition for Unit and Group out message 2025-04-22 20:57:06 +02:00
Thomas
326e27327d Merge pull request #2286 from FlightControl-Master/master
Kola airbase additions
2025-04-21 19:38:24 +02:00
Thomas
08d8f3e25f Merge pull request #2285 from shaji-Dev/master
[ADDED] New Kola airbases
2025-04-21 19:37:22 +02:00
shaji
6f72697e26 [ADDED] New Kola airbases
-- * AIRBASE.Kola.Alta
-- * AIRBASE.Kola.Sodankyla
-- * AIRBASE.Kola.Enontekio
-- * AIRBASE.Kola.Evenes
-- * AIRBASE.Kola.Hosio
2025-04-21 19:11:22 +02:00
Applevangelist
52c45364fc Merge remote-tracking branch 'origin/develop' into branch 2025-04-20 17:51:08 +02:00
Applevangelist
fcfc41252e Merge remote-tracking branch 'origin/master' into branch 2025-04-20 17:50:51 +02:00
Applevangelist
46f9cf46e4 xxx 2025-04-20 17:50:16 +02:00
Applevangelist
296d4b1e93 Merge remote-tracking branch 'origin/master' into develop 2025-04-20 17:49:54 +02:00
Applevangelist
0f6439cf9f #MANTIS - added C-RAM Point Defense 2025-04-20 17:49:25 +02:00
Applevangelist
9705b49dbe #PLAYERTASKMANAGER - Surfaced some functions for conflict zones 2025-04-20 15:52:30 +02:00
Applevangelist
1c24786723 xx 2025-04-20 15:51:44 +02:00
Applevangelist
9364579a18 #EASYGCICAP - fixed some wrongly used self.values
#OPSZONE - make check time schedule configureable
2025-04-19 17:53:07 +02:00
Applevangelist
7ded993c49 xx 2025-04-19 17:52:17 +02:00
Applevangelist
2636ca166f xx 2025-04-19 17:49:03 +02:00
Applevangelist
5d6e0c333b Merge remote-tracking branch 'origin/develop' into branch 2025-04-18 19:46:20 +02:00
Applevangelist
ec2a89fa48 Merge remote-tracking branch 'origin/master' into branch
# Conflicts:
#	Moose Development/Moose/Wrapper/Airbase.lua
2025-04-18 19:46:12 +02:00
Frank
02a6d8f2c0 Merge branch 'master' into develop 2025-04-18 17:50:11 +02:00
Frank
2c10943cb1 Update Airbase.lua
Germany map Umlaute
2025-04-18 17:50:01 +02:00
Frank
a293c59d6d Merge branch 'master' into develop 2025-04-18 17:46:00 +02:00
Frank
544db963ea Update Airbase.lua 2025-04-18 17:45:43 +02:00
Frank
d3fd21e6da Merge branch 'master' into develop 2025-04-18 17:41:07 +02:00
Frank
207698a2dd Update Airbase.lua
- Germany map readded Umlaute in keys
2025-04-18 17:40:55 +02:00
Applevangelist
27cd90a0ce Merge remote-tracking branch 'origin/master' into develop 2025-04-18 16:02:56 +02:00
Applevangelist
d1ae2c0f5e xx 2025-04-18 16:01:55 +02:00
Applevangelist
66e6a603f1 Merge remote-tracking branch 'origin/master' into develop 2025-04-18 14:46:45 +02:00
Applevangelist
0392417189 Germany CW Bases 2025-04-18 14:46:13 +02:00
Frank
0678ad17f8 Merge branch 'master' into develop 2025-04-17 21:34:17 +02:00
Frank
be4beea9d0 Update Airbase.lua
- Removed umlauts and ß in airbase names
2025-04-17 21:34:01 +02:00
Frank
5da899138b Merge pull request #2284 from FlightControl-Master/FF/MasterDevel
Germany CW map
2025-04-17 21:19:48 +02:00
Frank
1ec1e00bde Germany CW map
- ATIS Germany map
- UTILS magnetic declination
2025-04-17 21:19:01 +02:00
Thomas
d7d46d4f1b Merge pull request #2283 from FlightControl-Master/master
Merge from master
2025-04-17 08:45:51 +02:00
Thomas
5d93b33d42 Merge pull request #2282 from shaji-Dev/master
[FIXED] Returns nil for late activated templates we use to mark paths to use in our scripts
2025-04-17 08:44:38 +02:00
shaji
b2077bfc74 [FIXED] Returns nil for late activated templates we use to mark waypoint paths to use in the scripts. 2025-04-16 19:59:13 +02:00
Applevangelist
e958ca103a #EASYGCICAP - make conflict zones setup a bit more explic 2025-04-15 11:45:49 +02:00
Applevangelist
c9414d98da #EASYGCICAP - make conflict zones setup a bit more explicit 2025-04-15 11:45:22 +02:00
Applevangelist
7df7a34c32 Merge remote-tracking branch 'origin/develop' into branch 2025-04-14 12:24:05 +02:00
Applevangelist
1d9ef869a7 Merge remote-tracking branch 'origin/master' into develop 2025-04-14 12:23:37 +02:00
Applevangelist
6fdf9a649f #AIRBASE rescribe umlauts 2025-04-14 12:23:12 +02:00
Applevangelist
5d303752fe Merge remote-tracking branch 'origin/develop' into branch 2025-04-14 12:14:03 +02:00
Applevangelist
66cb7dd9b7 Merge remote-tracking branch 'origin/master' into branch
# Conflicts:
#	Moose Development/Moose/Wrapper/Airbase.lua
2025-04-14 12:13:55 +02:00
Applevangelist
e483d1bfca xx 2025-04-14 12:13:08 +02:00
Applevangelist
90c6c57449 Merge remote-tracking branch 'origin/master' into develop 2025-04-14 12:11:56 +02:00
Applevangelist
d013bbc751 #STORAGE Enum, some additions 2025-04-14 12:11:27 +02:00
Applevangelist
e0092fdba0 #AIRBASE GermanCW Map airbases 2025-04-14 11:37:45 +02:00
Thomas
ecce2eff9b Merge pull request #2281 from FlightControl-Master/master
Merge
2025-04-12 10:52:39 +02:00
Thomas
fbeada439f Merge pull request #2278 from leka1986/patch-5
Update Group.lua
2025-04-12 10:51:42 +02:00
Thomas
6c8858d2f5 Merge pull request #2280 from leka1986/patch-7
Update Set.lua
2025-04-12 10:51:00 +02:00
Thomas
e2b77878df Merge pull request #2279 from leka1986/patch-6
Update Event.lua
2025-04-12 10:50:39 +02:00
leka1986
53d7972858 Update Set.lua
Removed the Filter={},
2025-04-12 10:48:22 +02:00
leka1986
04a55e4104 Update Event.lua
Added nil checks which was causing nil. had this for a few weeks with no errors.
2025-04-12 10:44:29 +02:00
leka1986
d11acecdac Update Group.lua
Fix for the error attempt to index a nil value called from suppression
2025-04-12 10:26:34 +02:00
Applevangelist
1456ff37d0 Merge remote-tracking branch 'origin/develop' into branch 2025-04-09 08:17:12 +02:00
Applevangelist
abc86a7e9c Merge remote-tracking branch 'origin/master' into branch
# Conflicts:
#	Moose Development/Moose/Cargo/CargoGroup.lua
2025-04-09 08:17:06 +02:00
Applevangelist
4b23c86daa Merge remote-tracking branch 'origin/master' into develop 2025-04-09 08:16:20 +02:00
Applevangelist
49c11073e6 #MANTIS Mod data updates 2025-04-09 08:15:50 +02:00
Applevangelist
daa5caa125 Merge remote-tracking branch 'origin/master' into develop 2025-04-07 11:57:39 +02:00
Applevangelist
1a156e7e12 #CTLD - make menu build for CA a bit faster 2025-04-07 11:57:15 +02:00
Applevangelist
00c5d82590 xx 2025-04-07 11:56:54 +02:00
Applevangelist
6fe88a6319 Merge remote-tracking branch 'origin/master' into develop 2025-04-07 10:40:58 +02:00
Applevangelist
c902a1f601 xxx 2025-04-07 10:40:39 +02:00
Applevangelist
1856754614 #Smaller Changes 2025-04-07 10:40:30 +02:00
Thomas
b9f6c1b9c7 Merge pull request #2277 from FlightControl-Master/master
Update docs-header.py
2025-04-06 16:33:05 +02:00
Thomas
6ac452ff15 Update docs-header.py 2025-04-06 16:28:19 +02:00
Applevangelist
b52176a0ff Merge remote-tracking branch 'origin/master' into develop 2025-04-06 16:19:43 +02:00
Applevangelist
d707a4775c xx 2025-04-06 16:19:12 +02:00
Thomas
ffccc31e38 Update CargoGroup.lua 2025-04-06 16:12:13 +02:00
Thomas
0405af2bde Update docs-header.py 2025-04-06 16:04:07 +02:00
Thomas
d09f0b1f6f Update classes-core.md 2025-04-06 15:47:52 +02:00
Thomas
e50e572c78 Update classes-core.md 2025-04-06 15:43:09 +02:00
Applevangelist
d92a170304 Merge remote-tracking branch 'origin/develop' into branch 2025-04-06 15:39:50 +02:00
Applevangelist
41a9a1f74b Merge remote-tracking branch 'origin/master' into branch
# Conflicts:
#	Moose Development/Moose/Sound/RadioQueue.lua
2025-04-06 15:39:41 +02:00
Applevangelist
2e688e7da1 Merge remote-tracking branch 'origin/master' into develop 2025-04-06 15:38:55 +02:00
Applevangelist
3083599158 #CTLD - Allow CA Ground Transport 2025-04-06 15:38:26 +02:00
Applevangelist
9d7986e8d1 #CTLD - Allow CA Transport 2025-04-06 15:37:56 +02:00
Applevangelist
ff5adc29d9 xx 2025-04-03 14:22:53 +02:00
Applevangelist
2921f7a76b Merge remote-tracking branch 'origin/master' into develop 2025-04-03 14:22:25 +02:00
Applevangelist
b7b6c1ea19 #RADIOQUEUE - small tweak for a group when a unit dies. 2025-04-03 14:21:58 +02:00
Applevangelist
b77ff793a3 Merge remote-tracking branch 'origin/develop' into branch 2025-04-03 11:50:12 +02:00
Applevangelist
08b95ec8c6 Merge remote-tracking branch 'origin/master' into branch 2025-04-03 11:50:06 +02:00
Applevangelist
8a185c352e Merge remote-tracking branch 'origin/master' into develop 2025-04-03 11:49:30 +02:00
Applevangelist
5b107ce2da #CONTROLLABLE:CommandSmokeOnOff(OnOff, Delay) added 2025-04-03 11:48:53 +02:00
Applevangelist
2c255d48c5 #CONTROLLABLE:CommandSmokeOnOff(OnOff, Delay) added 2025-04-03 11:48:37 +02:00
Thomas
5c1e342a79 Update classes-core.md 2025-04-03 09:33:49 +02:00
Applevangelist
3fc8f52796 Merge remote-tracking branch 'origin/master' into develop 2025-04-03 09:29:57 +02:00
Applevangelist
367ee31135 xx 2025-04-03 09:29:38 +02:00
Applevangelist
ddf33da787 #DYNAMICCARGO - Hover / Sling checks 2025-04-03 09:29:19 +02:00
Applevangelist
4ca43b4779 Merge remote-tracking branch 'origin/develop' into branch 2025-04-03 09:16:41 +02:00
Applevangelist
d30b16a2fe Merge remote-tracking branch 'origin/master' into branch
# Conflicts:
#	Moose Development/Moose/Wrapper/DynamicCargo.lua
2025-04-03 09:16:32 +02:00
Thomas
e7cee4d97b Merge pull request #2276 from FlightControl-Master/master
Small fixes
2025-04-02 16:13:50 +02:00
Thomas
b0a192a767 Update DynamicCargo.lua
small fix
2025-04-02 16:12:06 +02:00
Thomas
986c340211 Merge pull request #2275 from FlightControl-Master/Applevangelist-patch-1
Update classes-core.md
2025-04-02 09:54:54 +02:00
Thomas
2109537f86 Update classes-core.md
fix
2025-04-02 09:54:20 +02:00
Thomas
bad9d1ea92 Merge pull request #2274 from FlightControl-Master/master
Fix SpawnStatic
2025-04-02 09:45:44 +02:00
Thomas
690db7f12f Merge pull request #2273 from FlightControl-Master/Applevangelist-SpawnStatic-1
Update SpawnStatic.lua
2025-04-02 09:43:04 +02:00
Thomas
4f3fd06cc9 Update SpawnStatic.lua
Restored previous version as overwritten
2025-04-02 09:42:02 +02:00
Applevangelist
f5b1050086 Merge remote-tracking branch 'origin/master' into develop 2025-04-01 15:27:55 +02:00
Applevangelist
4074023ed3 #DYNAMICCARGO - enhance checks for unloading cargo from hovering Hooks 2025-04-01 15:27:23 +02:00
Applevangelist
6cb306a7e1 #DYNAMICCARGO - enhance checks for unloading cargo from hovering Hooks 2025-04-01 15:27:14 +02:00
Applevangelist
b63185f1b3 Merge remote-tracking branch 'origin/develop' into branch 2025-04-01 14:15:13 +02:00
Applevangelist
4dc9e78812 Merge remote-tracking branch 'origin/master' into branch
# Conflicts:
#	Moose Development/Moose/AI/AI_Air.lua
#	Moose Development/Moose/AI/AI_Air_Engage.lua
#	Moose Development/Moose/AI/AI_Air_Patrol.lua
#	Moose Development/Moose/AI/AI_BAI.lua
#	Moose Development/Moose/AI/AI_Balancer.lua
#	Moose Development/Moose/AI/AI_CAP.lua
#	Moose Development/Moose/AI/AI_CAS.lua
#	Moose Development/Moose/AI/AI_Cargo_Airplane.lua
#	Moose Development/Moose/AI/AI_Cargo_Helicopter.lua
#	Moose Development/Moose/AI/AI_Formation.lua
#	Moose Development/Moose/AI/AI_Patrol.lua
#	Moose Development/Moose/Cargo/Cargo.lua
#	Moose Development/Moose/Cargo/CargoCrate.lua
#	Moose Development/Moose/Cargo/CargoGroup.lua
#	Moose Development/Moose/Cargo/CargoUnit.lua
#	Moose Development/Moose/Core/Point.lua
2025-04-01 14:15:06 +02:00
Applevangelist
f729b1d358 Merge remote-tracking branch 'origin/master' into develop 2025-04-01 14:13:42 +02:00
Applevangelist
6c00b0c7eb #POINT - some catches for POINT_VEC2 behaviour 2025-04-01 14:13:14 +02:00
Applevangelist
5adefe6f7b Merge remote-tracking branch 'origin/master' into develop 2025-04-01 13:18:55 +02:00
Applevangelist
22b81318b9 xx 2025-04-01 13:18:02 +02:00
Applevangelist
76dc0d690a #POINT - Removal of References to legacy POINT_VEC2/3 classes 2025-04-01 13:17:48 +02:00
Applevangelist
9862811c16 Merge remote-tracking branch 'origin/develop' into branch 2025-03-31 17:36:38 +02:00
Applevangelist
af12296e51 Merge remote-tracking branch 'origin/master' into branch 2025-03-31 17:36:32 +02:00
Thomas
13c16b8674 Merge pull request #2272 from shaji-Dev/develop
[Fixed] Parameter #1 (unit name) is incorrect
2025-03-31 14:05:41 +02:00
Thomas
222722225e Update OpsGroup.lua
Better make this a unit and alive check
2025-03-31 14:04:07 +02:00
shaji
82e1dcfc04 [Fixed] Parameter #1 (unit name) is incorrect 2025-03-31 13:11:56 +02:00
Thomas
c26220d1fd Merge pull request #2271 from FlightControl-Master/master
DynamicCargo small fixes
2025-03-31 12:32:55 +02:00
Thomas
d783f7be99 Merge pull request #2270 from FlightControl-Master/Applevangelist-patch-1
Update DynamicCargo.lua
2025-03-31 12:31:52 +02:00
Thomas
b66e91b11f Update DynamicCargo.lua
#DYNAMICCARGO small fixes
2025-03-31 12:31:23 +02:00
Applevangelist
0361a95f5d xx 2025-03-30 16:56:35 +02:00
Applevangelist
d6a16ee8b1 Merge remote-tracking branch 'origin/develop' into branch 2025-03-30 16:55:47 +02:00
Applevangelist
e3aade05d4 Merge remote-tracking branch 'origin/master' into branch
# Conflicts:
#	Moose Development/Moose/AI/AI_BAI.lua
#	Moose Development/Moose/AI/AI_Patrol.lua
2025-03-30 16:55:37 +02:00
Applevangelist
dd0b2ace65 Merge remote-tracking branch 'origin/master' into develop 2025-03-30 16:51:23 +02:00
Applevangelist
dc83af4d02 #SHORAD - fix typo for maxscootdist
#CTLD - slight refactoring of fixed wing support.
* Added `CTLD:AddAllowedFixedWingType(typename)` to add new types
* Renamed enabler flags into `enableFixedWing, FixedMinAngels, FixedMaxAngels, FixedMaxSpeed`
* Base support for Mosquito added
2025-03-30 16:50:43 +02:00
Applevangelist
c21c3ecb42 xx 2025-03-30 16:45:17 +02:00
Applevangelist
342e901dd1 Merge remote-tracking branch 'origin/master' into develop 2025-03-28 15:05:57 +01:00
Applevangelist
0a38700edb #AI_Patrol - small enhancement 2025-03-28 15:05:30 +01:00
Applevangelist
659615114a xx 2025-03-28 15:04:42 +01:00
Applevangelist
1bfeb0b41b Merge remote-tracking branch 'origin/develop' into branch 2025-03-27 11:10:17 +01:00
Applevangelist
4ec2923f46 Merge remote-tracking branch 'origin/master' into branch 2025-03-27 11:10:11 +01:00
Applevangelist
bfd26522d6 Merge remote-tracking branch 'origin/develop' into develop 2025-03-27 11:09:06 +01:00
Applevangelist
00d14c7c0a #PLAYERRECCE - small tweak for visual distance setting 2025-03-27 11:09:01 +01:00
Applevangelist
d2ec9642ca xx 2025-03-27 11:08:33 +01:00
Frank
47c9e1ba1f Update Legion.lua
- Dead assets are removed from cohort & legion
2025-03-23 22:45:15 +01:00
Frank
92e680a276 Merge branch 'master' into develop 2025-03-23 14:11:37 +01:00
Frank
4955fe4d92 Update Spawn.lua
- Fix for problem that helos are not spawned on ships but at origin of the map
2025-03-23 14:11:21 +01:00
Applevangelist
e8e4f6bcb9 Merge remote-tracking branch 'origin/develop' into branch 2025-03-22 14:59:41 +01:00
Applevangelist
f723f81c6d Merge remote-tracking branch 'origin/master' into branch 2025-03-22 14:59:34 +01:00
Applevangelist
e439bcdd0f Merge remote-tracking branch 'origin/master' into develop 2025-03-22 14:59:08 +01:00
Applevangelist
792aa73832 #AIBRASE - Afghanistan new Airbases 2025-03-22 14:58:24 +01:00
Applevangelist
d375e0ce29 #AUTOLASE - small fix for interval 2025-03-22 10:42:36 +01:00
Applevangelist
5aeb3ba42d xx 2025-03-22 10:42:17 +01:00
Applevangelist
104d0f3d2d Merge remote-tracking branch 'origin/develop' into branch 2025-03-21 09:23:31 +01:00
Applevangelist
0ec47795bf Merge remote-tracking branch 'origin/master' into branch 2025-03-21 09:23:24 +01:00
Applevangelist
c634023717 xxx 2025-03-21 09:22:59 +01:00
Applevangelist
e3670219ed Merge remote-tracking branch 'origin/master' into develop 2025-03-21 09:22:36 +01:00
Applevangelist
a915452e6e #COORDINATE - use magnetic for BRAA 2025-03-21 09:22:08 +01:00
Applevangelist
6662a1cf97 Merge remote-tracking branch 'origin/master' into develop 2025-03-16 16:57:38 +01:00
Applevangelist
be8405b72b #MANTIS - fix for data table 2025-03-16 16:57:11 +01:00
Applevangelist
9b24695377 Merge remote-tracking branch 'origin/master' into develop 2025-03-16 13:59:23 +01:00
Applevangelist
5ca3e3b2b8 #SHORAD tweak 2025-03-16 13:58:58 +01:00
Applevangelist
8bbef34b74 Merge remote-tracking branch 'origin/master' into develop 2025-03-16 13:07:19 +01:00
Applevangelist
618a8744a2 #MANTIS - improve point defense behaviour if not SAM to defend is around 2025-03-16 13:06:53 +01:00
Applevangelist
df9f204b7a xx 2025-03-16 13:06:19 +01:00
Applevangelist
1b29a30760 Merge remote-tracking branch 'origin/develop' into branch 2025-03-16 11:03:54 +01:00
Applevangelist
5d07cebdc4 Merge remote-tracking branch 'origin/master' into branch 2025-03-16 11:03:49 +01:00
Applevangelist
5329da32eb Merge remote-tracking branch 'origin/develop' into develop 2025-03-16 11:02:59 +01:00
Applevangelist
df86c3f2a1 #AUTOLASE - added a function to allow to set the monitoring report frequency 2025-03-16 11:02:55 +01:00
Applevangelist
b59bd0d4f2 xx 2025-03-16 11:02:32 +01:00
Frank
784fcb7882 Merge branch 'master' into develop 2025-03-15 22:49:57 +01:00
Frank
23aeef7a20 Airbase
- Airbase changes for helipads that are also airdromes
- Improved SpawnAtAirbase function
2025-03-15 22:49:43 +01:00
Applevangelist
a2ccfcf361 Merge remote-tracking branch 'origin/develop' into branch 2025-03-15 16:30:44 +01:00
Applevangelist
dccd46367f Merge remote-tracking branch 'origin/master' into branch 2025-03-15 16:30:36 +01:00
Applevangelist
0ee24b86dc Merge remote-tracking branch 'origin/master' into develop 2025-03-15 16:30:01 +01:00
Applevangelist
9ac4f136aa #MANTIS - extend scaling for short/point systems to make them more reactive 2025-03-15 16:29:24 +01:00
Applevangelist
ca9b2d79cc Merge remote-tracking branch 'origin/master' into develop 2025-03-15 10:56:34 +01:00
Applevangelist
c9a09c2fc9 #SOUND/MSRS - small fix to delete the "--ssml" tag if google provider is used in conjunction with a file based sound output. 2025-03-15 10:56:13 +01:00
Applevangelist
2a6df8434a #SOUND/MSRS - small fix to delete the "--ssml" tag if google provider is used in conjunction with a file based sound output. 2025-03-15 10:55:05 +01:00
Applevangelist
1661872bf8 Merge remote-tracking branch 'origin/develop' into branch 2025-03-14 10:41:51 +01:00
Applevangelist
8878d1a8d3 Merge remote-tracking branch 'origin/master' into branch 2025-03-14 10:41:45 +01:00
Applevangelist
813286f7f3 Merge remote-tracking branch 'origin/master' into develop 2025-03-14 10:41:13 +01:00
Applevangelist
6028c91f81 #NET small fix if PlayerSlot remains nil 2025-03-14 10:40:52 +01:00
Frank
11b6bb2638 Merge branch 'master' into develop 2025-03-13 16:41:25 +01:00
Frank
3c57928f46 Update Airbase.lua
- Fixed bug in counting the number of parking spots per terminal type
2025-03-13 16:40:57 +01:00
Applevangelist
e091e659a2 Merge remote-tracking branch 'origin/master' into develop 2025-03-13 10:51:00 +01:00
Applevangelist
32f0bb33c3 #ZONE - Trigger added OnAfterZoneEmpty and OnAfterObjectDead 2025-03-13 10:50:15 +01:00
Applevangelist
8e017f2ce0 #ZONE Some Trigger Improvements, added OnAfterZoneEmpty 2025-03-13 10:49:11 +01:00
Applevangelist
d16124213f xxx 2025-03-12 10:25:54 +01:00
Applevangelist
bdff8541b4 Merge remote-tracking branch 'origin/develop' into branch 2025-03-12 09:19:23 +01:00
Applevangelist
0d0cc20ebc Merge remote-tracking branch 'origin/master' into branch 2025-03-12 09:19:15 +01:00
Applevangelist
dea64751c3 Merge remote-tracking branch 'origin/master' into develop 2025-03-12 09:18:43 +01:00
Applevangelist
31d0410284 #TEMPLATES remove files 2025-03-12 08:56:13 +01:00
Applevangelist
6fcb4a3184 Merge remote-tracking branch 'origin/master' into branch
# Conflicts:
#	Moose Setup/Moose.files
2025-03-12 08:46:16 +01:00
Applevangelist
9cdf550432 #PLAYERTASKMANAGER - Allow use of multiple drones for lasing of precision bombing tasks 2025-03-12 08:45:08 +01:00
Applevangelist
09525029ab Merge remote-tracking branch 'origin/master' into develop
# Conflicts:
#	Moose Development/Moose/Modules.lua
#	Moose Setup/Moose.files
2025-03-12 08:44:00 +01:00
Applevangelist
87c436ba34 #STTS - remove from tree 2025-03-12 08:42:38 +01:00
Applevangelist
2fced89dfc xx 2025-03-11 18:31:16 +01:00
Applevangelist
96ad3ff98b xx 2025-03-11 16:32:01 +01:00
Applevangelist
b1002017e5 Merge remote-tracking branch 'origin/master' into develop 2025-03-11 16:30:07 +01:00
Applevangelist
6e9727e265 xx 2025-03-11 16:29:31 +01:00
Applevangelist
383e953ba1 Merge remote-tracking branch 'origin/master' into develop 2025-03-11 10:52:40 +01:00
Applevangelist
94ee76fe62 #CONTROLLABLE - Improved IR Markers 2025-03-11 10:52:16 +01:00
Applevangelist
6c8fa5585b #CONTROLLABLE - Improved IR Markers 2025-03-11 10:51:44 +01:00
Applevangelist
dc8992134e Merge remote-tracking branch 'origin/develop' into branch 2025-03-11 09:36:35 +01:00
Applevangelist
885b329dcb Merge remote-tracking branch 'origin/master' into branch 2025-03-11 09:36:28 +01:00
Frank
9b52b640e6 Merge branch 'master' into develop 2025-03-10 19:53:51 +01:00
Frank
ea23162ca9 Update Spawn.lua
- Enabled explicit parking spots in SpawnAtAirbase
2025-03-10 19:53:24 +01:00
Thomas
68d0220912 Merge pull request #2268 from FlightControl-Master/master
merge from master
2025-03-10 15:35:08 +01:00
Thomas
702c5464aa Merge pull request #2267 from FlightControl-Master/master
merge from master
2025-03-10 15:33:52 +01:00
Thomas
683388faee Merge pull request #2266 from FlightControl-Master/Applevangelist-IR-1
Update Controllable.lua
2025-03-10 15:32:07 +01:00
Thomas
56ec3920c5 Update Controllable.lua
IR Strobe . some improvements
2025-03-10 13:12:00 +01:00
Thomas
88842d1c67 Merge pull request #2265 from FlightControl-Master/master
Update CTLD.lua
2025-03-10 10:57:19 +01:00
Thomas
f335ffc4ec Update CTLD.lua
Clarify hover min/max height is in meters
2025-03-10 10:55:01 +01:00
Applevangelist
ac750aff10 Merge remote-tracking branch 'origin/develop' into branch 2025-03-09 14:38:49 +01:00
Applevangelist
c679a77f9f Merge remote-tracking branch 'origin/master' into branch 2025-03-09 14:38:39 +01:00
Applevangelist
176e4f6217 xxx 2025-03-09 14:38:16 +01:00
Applevangelist
f76aefa976 Merge remote-tracking branch 'origin/master' into develop 2025-03-09 14:37:56 +01:00
Applevangelist
4976cd86f2 ZONE_ELASTIC - function to remove vertices 2025-03-09 14:36:42 +01:00
Frank
2cd66ae1d4 Merge branch 'master' into develop 2025-03-08 21:37:59 +01:00
Frank
c00eff8b23 AIRBASE
- AIRBASE: Workaround for DCS bug that helipads have category of airdrome
- SET_AIRBASE: Added FilterZones function
2025-03-08 21:37:43 +01:00
Applevangelist
7f0376561b Merge remote-tracking branch 'origin/master' into develop 2025-03-06 14:52:15 +01:00
Applevangelist
3c710613a8 CONTROLLABLE added HasIRMarker 2025-03-06 14:51:49 +01:00
Applevangelist
9e69c1c17e xx 2025-03-06 14:51:34 +01:00
Applevangelist
187a620f37 Merge remote-tracking branch 'origin/develop' into branch
# Conflicts:
#	Moose Development/Moose/AI/AI_CAS.lua
2025-03-06 12:28:39 +01:00
Applevangelist
91b2fc7d9c Merge remote-tracking branch 'origin/master' into branch 2025-03-06 12:27:45 +01:00
Applevangelist
3585208547 Merge remote-tracking branch 'origin/master' into develop
# Conflicts:
#	Moose Development/Moose/Core/Zone.lua
2025-03-06 12:27:08 +01:00
Applevangelist
29c0017e80 xx 2025-03-06 12:26:24 +01:00
Applevangelist
45ebf9a3c7 #ZONE_ELASTIC - Fix zone filling 2025-03-06 12:25:57 +01:00
Applevangelist
0baa912465 xx 2025-03-06 12:25:34 +01:00
Frank
908e505ce3 Merge branch 'master' into develop 2025-03-05 20:48:28 +01:00
Frank
c808e4a4e2 Update Airbase.lua
- Added FighterAircraftSmall parking spot type
2025-03-05 20:48:15 +01:00
Thomas
e129eb97a7 Merge pull request #2263 from FlightControl-Master/master
ATIS
2025-03-03 14:21:02 +01:00
Thomas
e2612b97d7 Merge pull request #2262 from FlightControl-Master/Applevangelist-Atis-Kola
Update ATIS.lua
2025-03-03 14:19:45 +01:00
Thomas
70e9d91bb5 Update ATIS.lua 2025-03-03 14:19:11 +01:00
Thomas
0b8810f8b3 Update ATIS.lua
ATIS - Fix for Kola map when no sunset or sunrise arise and SRS is not used
2025-03-03 14:16:47 +01:00
Applevangelist
3d39ccbdce Merge remote-tracking branch 'origin/master' into develop 2025-03-02 12:40:30 +01:00
Applevangelist
41b867a4ca #GROUP - ensure GROUP:GetCoordinate() returns a group heading data field 2025-03-02 12:40:00 +01:00
Applevangelist
79e7f9940e xx 2025-03-02 12:39:25 +01:00
Applevangelist
427baa43d7 Merge remote-tracking branch 'origin/master' into develop 2025-02-23 16:12:14 +01:00
Applevangelist
d4e141f3c5 #EASYGCICAP - use onbefore on foreign classes rather than OnAfter to avoid user overwrites 2025-02-23 16:11:36 +01:00
Applevangelist
1c0a8d9380 #SET_CLIENT - added option to handle CA slots
#CONTROLLABLE - fix typos
2025-02-23 16:10:32 +01:00
Applevangelist
25845c4d22 xxx 2025-02-23 16:09:41 +01:00
Applevangelist
b66a342642 xxx 2025-02-23 11:12:00 +01:00
Thomas
f05680f23d Merge pull request #2259 from FlightControl-Master/master
Master merge
2025-02-23 08:40:49 +01:00
Thomas
2b0b9d44eb Merge pull request #2258 from leka1986/patch-3
Update Controllable.lua
2025-02-23 08:40:00 +01:00
leka1986
2fc7a3b542 Update Controllable.lua
Fixing this error
Line 26056: attempt to index local '_coord' (a nil value)
in this line,
local _tocoord=_coord:GetRandomCoordinateInRadius(_radius,100)
2025-02-23 02:34:04 +01:00
Applevangelist
820ae2759b Merge remote-tracking branch 'origin/master' into develop 2025-02-22 16:36:52 +01:00
Applevangelist
12b596a47f #MANTIS - if no EWR system is left over, MANTIS will switch on a random SR/TR each cycle to detect incoming enemies. 2025-02-22 16:36:15 +01:00
Applevangelist
e2054371ed Merge remote-tracking branch 'origin/develop' into branch 2025-02-21 10:44:25 +01:00
Applevangelist
e9c1ccedcd Merge remote-tracking branch 'origin/master' into branch 2025-02-21 10:44:17 +01:00
Applevangelist
a06d099917 Merge remote-tracking branch 'origin/master' into develop 2025-02-21 10:43:45 +01:00
Applevangelist
7552309a28 #AUTOLASE - option to make ground units aware of target to be lased 2025-02-21 10:43:14 +01:00
Applevangelist
b9ad5b5ba7 #TARGET - add threat level determination for ZONE and OPSZONE 2025-02-21 10:42:52 +01:00
Applevangelist
367d1c6715 xx 2025-02-21 10:41:46 +01:00
Applevangelist
8ef781a9ac #SET_BASE - added GetThreatLevelMax() 2025-02-21 10:39:21 +01:00
Applevangelist
093c3fe7c9 #AUTOLASE - improve detection for ground Recce 2025-02-20 11:40:01 +01:00
Applevangelist
428712147d Merge remote-tracking branch 'origin/develop' into branch 2025-02-19 17:34:00 +01:00
Applevangelist
20fa78bc85 Merge remote-tracking branch 'origin/master' into branch 2025-02-19 17:33:53 +01:00
Applevangelist
5e24e8658b Merge remote-tracking branch 'origin/master' into develop 2025-02-19 17:33:17 +01:00
Applevangelist
43eeaede65 #SET - Error output for Addif the ObjectName variable is empty
#UNIT - Return zero is SpeedMax is nil
2025-02-19 17:32:37 +01:00
Frank
749c5f87de Merge pull request #2257 from leka1986/patch-2
Update Range.lua
2025-02-18 21:37:37 +01:00
leka1986
a520daeb56 Update Range.lua
attempt to index local 'target' (a nil value)

Was using this build in another mission. Added a nilcheck.
env.info('*** MOOSE GITHUB Commit Hash ID: 2025-02-14T06:13:08+01:00-24b320077721d45774acd56b25086ef6bdfb2e5a ***')
2025-02-18 21:23:58 +01:00
Applevangelist
9bb713db10 Merge remote-tracking branch 'origin/master' into develop 2025-02-18 11:14:00 +01:00
Applevangelist
f9ba96f228 #MANTIS - New 4-Tier-Approach 2025-02-18 11:13:23 +01:00
Applevangelist
d081e115c7 #MANTIS 4-Tier-Approach 2025-02-18 11:12:14 +01:00
Applevangelist
3df9b54c01 Merge remote-tracking branch 'origin/develop' into branch 2025-02-17 08:36:24 +01:00
Applevangelist
72ac957806 Merge remote-tracking branch 'origin/master' into branch
# Conflicts:
#	Moose Development/Moose/Functional/ATC_Ground.lua
2025-02-17 08:36:15 +01:00
Applevangelist
a49bd23a2a #ATC_GROUND 2025-02-17 08:35:30 +01:00
Thomas
913b2c6d3f Merge pull request #2256 from FlightControl-Master/master
CTLD
2025-02-17 07:01:08 +01:00
Thomas
cea2f18228 Merge pull request #2255 from leka1986/patch-1
Update CTLD.lua
2025-02-17 07:00:18 +01:00
leka1986
fd2d8a5119 Update CTLD.lua
Now in the listcargo, (cargo onboard) I will display CargoName and a number / number instead of listing each crate, 5 lines for 5 crates. 

Now 1 line for each CargoName and 2/2 or 3/3, etc.

"Rescan" have been moved to only show up if it detects lesser then what it's needed when loading.
2025-02-16 17:16:05 +01:00
Thomas
24b3200777 Merge pull request #2254 from FlightControl-Master/master
Tld
2025-02-14 06:13:08 +01:00
Thomas
fa4e0447dd Merge pull request #2253 from leka1986/patch-1
Update CTLD.lua
2025-02-14 06:11:49 +01:00
leka1986
31aa604fc4 Update CTLD.lua
Added _LoadSingleCrateSet Function
Added _refreshLoadCratesMenu Function
Added Event for Takeoff, Land.

Changes to the RefreshF10menus function
the menus does not gets deleted, and rebuild, but only what is inside them. 
So they will remain at the same position. 

tested multiple times
2025-02-14 00:20:23 +01:00
Applevangelist
3518c89791 Merge remote-tracking branch 'origin/master' into develop 2025-02-10 18:03:22 +01:00
Applevangelist
8ea746ee19 xxx 2025-02-10 18:02:57 +01:00
Applevangelist
f44db27565 #ATC_GROUND_UNIVERSAL
* Correct usage of airbase names if given
* Exclude FARPs and Ships
2025-02-10 18:02:42 +01:00
Thomas
acaab0c6a9 Merge pull request #2251 from FlightControl-Master/master
CTLD
2025-02-10 06:02:33 +01:00
Thomas
9b1abab73a Merge pull request #2250 from leka1986/master
Update CTLD.lua
2025-02-10 06:01:30 +01:00
leka1986
cad8f15b61 Update CTLD.lua
Reworked the logic of the menu.
Now it will Show be shown :
1. Ammo truck
2. Humvee
3. Ammo truck 1/2 -- 1/2 due to incomplete set.

And for the troops

Squad 8 (2) -- 2 set of squad 8, Selecting this will only deploy 1 set.
Squad 16

Changed so when loading troops, it will state 
Squad 8 boarded. same for the extraction.

I have tested it and it works
Heart8reaker also tested it and no issues so far.
2025-02-09 23:41:16 +01:00
Applevangelist
b2bc553e48 Merge remote-tracking branch 'origin/develop' into branch 2025-02-09 12:25:23 +01:00
Applevangelist
a91c76a0be Merge remote-tracking branch 'origin/master' into branch 2025-02-09 12:25:18 +01:00
Applevangelist
aafbd8c297 Merge remote-tracking branch 'origin/master' into develop 2025-02-09 12:24:41 +01:00
Applevangelist
1d08bcf2e0 #CTLD docu 2025-02-09 12:24:16 +01:00
Applevangelist
5a94be98fe Merge remote-tracking branch 'origin/develop' into branch 2025-02-09 12:06:45 +01:00
Applevangelist
999d67b7af Merge remote-tracking branch 'origin/master' into branch 2025-02-09 12:06:38 +01:00
Applevangelist
6d38346eb8 Merge remote-tracking branch 'origin/master' into develop
# Conflicts:
#	Moose Development/Moose/Ops/CTLD.lua
2025-02-09 12:05:57 +01:00
Applevangelist
9487a5ae91 #CTLD 2025-02-09 12:05:18 +01:00
Thomas
d2bb6a4fc0 Merge pull request #2248 from leka1986/patch-4
Update CTLD.lua
2025-02-09 12:04:39 +01:00
Applevangelist
91e7c86ba0 Merge remote-tracking branch 'origin/develop' into branch 2025-02-09 12:03:23 +01:00
Applevangelist
f75c464bcd Merge remote-tracking branch 'origin/master' into branch
# Conflicts:
#	Moose Development/Moose/Ops/CTLD.lua
2025-02-09 12:03:16 +01:00
Applevangelist
a4ae0d88e1 xx 2025-02-09 12:02:22 +01:00
Applevangelist
c3ccecdd4a #AWACS - Escort vector catch for noobs not reading the description 2025-02-09 12:01:57 +01:00
leka1986
5ad54648ab Update CTLD.lua
Removed the block from partly drop 1. 

It's not helpful for the user to have 1 incomplete a set of crates onboard. 
With this line removed, If someone loads 2 items, and second item is not completed, due to crate limit or due weight, player can unload the uncomplete set.
2025-02-09 11:51:34 +01:00
Applevangelist
96337cc5df #CTLD 2025-02-09 11:48:22 +01:00
Applevangelist
5c5b1383be Merge remote-tracking branch 'origin/develop' into branch 2025-02-09 11:47:15 +01:00
Applevangelist
a5e75bf485 Merge remote-tracking branch 'origin/master' into branch 2025-02-09 11:47:08 +01:00
Thomas
fa0ce0927b Merge pull request #2247 from leka1986/patch-3
Update CTLD.lua
2025-02-09 11:34:58 +01:00
leka1986
693c2a730f Update CTLD.lua
Added  self:_RefreshDropCratesMenu(Group, Unit) to  function CTLD:_LoadCratesNearby(Group, Unit)
2025-02-09 11:26:38 +01:00
Thomas
0d438a9452 Merge pull request #2246 from FlightControl-Master/master
Ctd changes
2025-02-09 09:23:25 +01:00
Thomas
bc9eee22b7 Merge pull request #2245 from FlightControl-Master/Applevangelist-patch-1
Update CTLD.lua
2025-02-08 14:29:59 +01:00
Thomas
f74d25b31c Update CTLD.lua
Option to unload single cargo items by @lekaa
2025-02-08 14:29:35 +01:00
Applevangelist
66af360ce6 Merge remote-tracking branch 'origin/master' into develop 2025-02-06 16:36:27 +01:00
Applevangelist
c6d79db266 Merge remote-tracking branch 'origin/develop' into branch 2025-02-06 16:35:58 +01:00
Applevangelist
b394622b92 Merge remote-tracking branch 'origin/master' into branch 2025-02-06 16:35:49 +01:00
Applevangelist
ad9893c3ab Merge remote-tracking branch 'origin/develop' into develop 2025-02-06 16:35:03 +01:00
Thomas
1156971d94 Merge pull request #2243 from shaji-Dev/master
[ADDED] `UNIT:IsEWR()`
2025-02-06 12:22:18 +01:00
Shafik
c79b5c37c4 [ADDED] UNIT:IsEWR() 2025-02-06 13:16:03 +02:00
Applevangelist
03c623a0c2 xx 2025-02-06 08:52:52 +01:00
Thomas
b4e6201b68 Merge pull request #2242 from FlightControl-Master/Applevangelist-patch-1
Update Autolase.lua
2025-02-06 08:52:20 +01:00
Thomas
f26d8d6822 Update Autolase.lua
Added option to set offset smoke parameter
2025-02-06 08:52:05 +01:00
Applevangelist
3d0b7b9267 #AUTOLASE - Add offset parameters for autolase:EnableSmokeMenu({Angle=math.random(0,359),Distance=math.random(10,20)}) 2025-02-06 08:50:33 +01:00
Thomas
a2b650a9e3 Merge pull request #2241 from shaji-Dev/master
[FIXED] attempt to index field  (a nil value)
2025-02-03 12:03:04 +01:00
Shafik
905b442e9b [FIXED] attempt to index field (a nil value) 2025-02-03 09:03:43 +02:00
Applevangelist
a5a0d8ab8a Merge remote-tracking branch 'origin/develop' into branch
# Conflicts:
#	Moose Development/Moose/Functional/Tiresias.lua
2025-02-02 12:56:35 +01:00
Applevangelist
3bb5d3709b Merge remote-tracking branch 'origin/master' into branch 2025-02-02 12:56:05 +01:00
Thomas
7cd95377f9 Merge pull request #2240 from FlightControl-Master/master
Merge from master
2025-02-02 12:54:42 +01:00
Thomas
18fe3112bd Merge pull request #2238 from shaji-Dev/master
[ADDED] Set AI On/Off for units
2025-02-02 12:53:37 +01:00
Thomas
84e85dd0b5 Merge pull request #2239 from shaji-Dev/develop
Develop
2025-02-02 12:53:25 +01:00
Shafik
2744befbab [FIXED] Group not turning off due to conflicting condition checking true and false (if grp.Tiresias and grp.Tiresias.invisible and grp.Tiresias.invisible == false then)
[FIXED] Units turning on for same coalition aircrafts in radius
2025-02-02 13:27:03 +02:00
Shafik
e87dc525b9 [FIXED] Group not turning off due to conflicting condition checking true and false (if grp.Tiresias and grp.Tiresias.invisible and grp.Tiresias.invisible == false then)
[FIXED] Units turning on for same coalition aircrafts in radius
2025-02-02 13:25:53 +02:00
Shafik
cc057744ba [ADDED] UNIT:IsSAM() and UNIT:IsAAA() to use used in their respective GROUP functions. 2025-02-02 12:46:55 +02:00
Shafik
db1779e1db [ADDED] Set AI On/Off for units 2025-02-02 11:42:40 +02:00
Applevangelist
ed31b87b2f Merge remote-tracking branch 'origin/master' into develop 2025-02-01 18:40:44 +01:00
Applevangelist
d1e31a3652 #PLAYERRECCE - add parameter for SRS Backend option 2025-02-01 18:40:26 +01:00
Applevangelist
d25a723fc7 #MESSAGE - add parameter for SRS Backend 2025-02-01 18:39:51 +01:00
Applevangelist
32e538a27d xx 2025-02-01 18:39:30 +01:00
Applevangelist
02b75bf529 Merge remote-tracking branch 'origin/develop' into branch 2025-01-31 14:28:08 +01:00
Applevangelist
f3477a03c6 Merge remote-tracking branch 'origin/master' into branch 2025-01-31 14:28:00 +01:00
Applevangelist
3fb4cae7b8 Merge remote-tracking branch 'origin/master' into develop 2025-01-31 14:27:25 +01:00
Applevangelist
f0fe1b431d #SPAWN - Add optional waiting time to InitRepeatOnLanding 2025-01-31 14:26:32 +01:00
Thomas
8e286edd25 Merge pull request #2237 from shaji-Dev/master
[FIXED] attempt to index a nil value
2025-01-31 10:04:01 +01:00
Shafik
74520b1359 [FIXED] attempt to index a nil value (AWACS:_CheckAwacsStatus -> OPSGROUP:GetCallsignName -> GROUP:IsPlayer) 2025-01-31 10:30:18 +02:00
shaji-Dev
0ffcdf2f66 Merge branch 'FlightControl-Master:develop' into develop 2025-01-31 10:21:37 +02:00
Applevangelist
fe0edeb011 Merge remote-tracking branch 'origin/master' into develop 2025-01-30 18:33:06 +01:00
Applevangelist
d06e44d37b #CTLD small additions 2025-01-30 18:32:39 +01:00
Applevangelist
35348d9b81 #UTILS - Added UTILS.Weather for fog stuff 2025-01-30 18:32:25 +01:00
Applevangelist
8f5e519d0a xxx 2025-01-30 18:32:01 +01:00
Applevangelist
1c4c5b5476 Merge remote-tracking branch 'origin/develop' into branch 2025-01-30 13:21:17 +01:00
Applevangelist
85e4a6cbd6 Merge remote-tracking branch 'origin/master' into branch 2025-01-30 13:21:10 +01:00
Applevangelist
d06a618582 Merge remote-tracking branch 'origin/master' into develop 2025-01-30 13:20:35 +01:00
Applevangelist
4b16e94eaf #CTLD - Added function for the game designer to get an overview of stock and alive groups, if they want to manage the stock: CTLD:_CountStockPlusInHeloPlusAliveGroups() 2025-01-30 13:19:11 +01:00
Applevangelist
0f9742aeb4 xx 2025-01-30 13:14:47 +01:00
Applevangelist
2d49745215 Merge remote-tracking branch 'origin/develop' into branch 2025-01-30 09:06:11 +01:00
Applevangelist
2b6a039368 Merge remote-tracking branch 'origin/master' into branch
# Conflicts:
#	Moose Development/Moose/Core/Base.lua
2025-01-30 09:05:56 +01:00
Applevangelist
d5253f0420 Merge remote-tracking branch 'origin/master' into develop 2025-01-30 09:04:46 +01:00
Thomas
d983676330 Merge pull request #2236 from Fedge7/fedge/enums-weapons
Fixes AH-64D FCR enum value, adds APKWS.
2025-01-30 06:56:44 +01:00
Fedge
03c30f3cce Adds the 2 primary APKWS rockets as entries in the ENUMS.Storage.weapons.missiles table.
I added them to `missiles`, rather than `nurs` because that's where they appear in the mission editor's storage window.
2025-01-29 18:48:34 -07:00
Fedge
99b93266ad Fixes the AH-64D's FCR enum weapon definition. 2025-01-29 18:47:44 -07:00
Thomas
4530b74367 Merge pull request #2235 from FlightControl-Master/master
merge from master
2025-01-29 15:31:29 +01:00
Thomas
e307b57e67 Merge pull request #2234 from shaji-Dev/master
nil fixes
2025-01-29 13:58:42 +01:00
Shafik
5ef9bb2acd [FIXED] attempt to get length of local 'parking' (a nil value) 2025-01-29 14:44:31 +02:00
Shafik
4aacdc1567 [FIXED] attempt to index local 'CleanUpUnit' (a nil value) 2025-01-29 14:43:43 +02:00
Thomas
57552f4300 Merge pull request #2233 from FlightControl-Master/Applevangelist-patch-3
Update CTLD.lua
2025-01-29 13:24:45 +01:00
Thomas
09d53f7d8c Update CTLD.lua
#CTLD - Added HelicopterLost Event
2025-01-29 13:24:04 +01:00
Shafik
161bdc0413 [FIXED] attempt to concatenate field 'IniGroupName' (a nil value) ?? 2025-01-29 10:13:34 +02:00
Shafik
44ad362dbc [FIXED] attempt to index local 'CleanUpUnit' (a nil value) 2025-01-29 10:06:19 +02:00
Shafik
ea776aeacc [FIXED] attempt to get length of local 'parking' (a nil value) 2025-01-29 10:05:55 +02:00
Frank
e30479329a Merge branch 'master' into develop 2025-01-28 20:29:45 +01:00
Frank
d9948d1a19 Update Group.lua 2025-01-28 20:29:35 +01:00
Frank
b962096661 Update RAT.lua
- Tried to fix bug the Pnow is nil
2025-01-28 20:29:04 +01:00
Frank
725b55a505 Merge branch 'master' into develop 2025-01-28 20:22:57 +01:00
Frank
5f7a4f2bbb Update Group.lua
- Added way to get the coordinate of the group
2025-01-28 20:22:45 +01:00
Thomas
f555399e2f Merge pull request #2231 from FlightControl-Master/master
Master merge
2025-01-28 08:32:34 +01:00
Thomas
1b6945e0b0 Merge pull request #2230 from FlightControl-Master/Applevangelist-patch-2
Update Scenery.lua
2025-01-28 08:31:33 +01:00
Thomas
4fd55b1bd6 Update Scenery.lua
#SCENERY
2025-01-28 08:31:17 +01:00
Applevangelist
fb276cf812 Merge remote-tracking branch 'origin/master' into develop 2025-01-27 18:49:01 +01:00
Applevangelist
41dfaab82a xx 2025-01-27 18:48:39 +01:00
Applevangelist
83e146f207 xx 2025-01-27 18:46:52 +01:00
Thomas
20b8deb6de Merge pull request #2228 from FlightControl-Master/Applevangelist-patch-2
Update CTLD.lua
2025-01-27 18:46:07 +01:00
Thomas
6af836c118 Update CTLD.lua 2025-01-27 18:45:39 +01:00
Thomas
f87b8a2c2a Merge pull request #2229 from FlightControl-Master/Applevangelist-patch-3
Update Base.lua
2025-01-27 18:44:50 +01:00
Thomas
daffd7412a Update Base.lua 2025-01-27 18:44:35 +01:00
Thomas
bfb60b318e Update CTLD.lua
update
2025-01-27 13:27:13 +01:00
Thomas
a55959dfbb Update Base.lua
Added property functions
2025-01-27 12:00:25 +01:00
Thomas
4d24eb82be Update CTLD.lua
#CTLD - Add the group name of the extracted group to TroopsExtracted FSM event
2025-01-27 08:51:17 +01:00
Applevangelist
b115a0a672 Merge remote-tracking branch 'origin/develop' into branch 2025-01-26 17:35:56 +01:00
Applevangelist
0c384a75cc Merge remote-tracking branch 'origin/master' into branch 2025-01-26 17:35:51 +01:00
Applevangelist
c19d20c3f7 xxx 2025-01-26 17:35:30 +01:00
Applevangelist
4656d3e019 Merge remote-tracking branch 'origin/develop' into develop 2025-01-26 17:35:11 +01:00
Applevangelist
86b76ba216 Merge remote-tracking branch 'origin/master' into develop 2025-01-26 17:35:06 +01:00
Applevangelist
ff561410ad xx 2025-01-26 17:34:36 +01:00
Applevangelist
e26caa2f74 smaller fixes 2025-01-26 17:34:22 +01:00
Applevangelist
c7bc55d851 Merge remote-tracking branch 'origin/develop' into branch 2025-01-26 14:55:59 +01:00
Applevangelist
245dd6bb27 Merge remote-tracking branch 'origin/master' into branch 2025-01-26 14:55:47 +01:00
Applevangelist
b32d17c2ab Merge remote-tracking branch 'origin/develop' into develop 2025-01-26 14:55:24 +01:00
Applevangelist
29111e1018 xx 2025-01-26 14:55:21 +01:00
Applevangelist
3433ebd665 Merge remote-tracking branch 'origin/master' into develop 2025-01-26 14:54:53 +01:00
Applevangelist
b75fff60c8 xx 2025-01-26 14:54:19 +01:00
Applevangelist
a47fa3f9fc Merge remote-tracking branch 'origin/master' into develop 2025-01-26 13:54:59 +01:00
Applevangelist
4ac57fce7a #CTLD - Added CTLD:GetGenericCargoObjectFromGroupName(GroupName) to get the generic CTLD_CARGO entry from a group name. 2025-01-26 13:54:28 +01:00
Applevangelist
6218b94c99 Merge remote-tracking branch 'origin/develop' into develop 2025-01-26 13:29:57 +01:00
Applevangelist
74a6c89801 Merge remote-tracking branch 'origin/master' into develop 2025-01-26 13:29:53 +01:00
Applevangelist
25a9a0120a #CTLD
- If stock is set, show stock number in menu entries
- Corrected table build for GetStockCrates/Troops/Statics
2025-01-26 13:29:18 +01:00
Thomas
deb0747e66 Merge pull request #2227 from shaji-Dev/develop
[FIXED] `bad argument #2 to 'format' (number expected, got nil)`
2025-01-26 08:38:11 +01:00
Shafik
00f5fde5a8 [FIXED] bad argument #2 to 'format' (number expected, got nil) 2025-01-26 00:19:19 +02:00
Applevangelist
ff4f162ca2 Merge remote-tracking branch 'origin/develop' into branch 2025-01-25 15:58:06 +01:00
Applevangelist
baf3f9a9a4 Merge remote-tracking branch 'origin/master' into branch
# Conflicts:
#	Moose Development/Moose/Ops/CTLD.lua
2025-01-25 15:57:59 +01:00
Applevangelist
03cd354f9e Merge remote-tracking branch 'origin/master' into develop 2025-01-25 15:55:01 +01:00
Applevangelist
ae65fce9cd xx 2025-01-25 15:54:42 +01:00
Applevangelist
66a1fa8af5 #CTLD - small fix for LoadesGroupsTable inserts 2025-01-25 15:54:29 +01:00
Applevangelist
5e856b67a1 Merge remote-tracking branch 'origin/develop' into branch 2025-01-25 15:07:38 +01:00
Applevangelist
f365c5153b Merge remote-tracking branch 'origin/master' into branch 2025-01-25 15:07:16 +01:00
Applevangelist
c2096c8dfd Merge remote-tracking branch 'origin/master' into develop 2025-01-25 15:06:40 +01:00
Applevangelist
1b4033cfce CTLD Small fix for PlayerTask ExtractTroops 2025-01-25 15:04:33 +01:00
Applevangelist
a11cda4ed1 Merge remote-tracking branch 'origin/develop' into branch 2025-01-24 12:26:38 +01:00
Applevangelist
9faa1b806d Merge remote-tracking branch 'origin/master' into branch 2025-01-24 12:26:30 +01:00
Applevangelist
750edf1144 Merge remote-tracking branch 'origin/master' into develop 2025-01-24 12:22:24 +01:00
Applevangelist
f533d4ec8f xxx 2025-01-24 12:22:00 +01:00
Applevangelist
48bc41873a #STORAGE - Added small helper to get Syria "H" Helipad warehouses via a zone 2025-01-24 12:21:44 +01:00
Applevangelist
2e54f51229 Merge remote-tracking branch 'origin/develop' into develop 2025-01-24 10:30:29 +01:00
Applevangelist
e39e414e0d Merge remote-tracking branch 'origin/master' into develop 2025-01-24 10:30:25 +01:00
Applevangelist
068a1ab99c #CTLD
- Added `OnAfterLoaded` which gives you access to the rebuild troops and vehicles in a table  of loaded groups, each entry is a table with three values: Group, TimeStamp and CargoType
2025-01-24 10:29:52 +01:00
Applevangelist
afc0671aa9 xx 2025-01-24 10:26:51 +01:00
Thomas
e35d9eb07f Merge pull request #2226 from FlightControl-Master/master
merge from master
2025-01-22 09:45:25 +01:00
Thomas
6551383070 Merge pull request #2225 from FlightControl-Master/Applevangelist-ZoneScaen-1
Update Zone.lua
2025-01-22 09:44:27 +01:00
Thomas
b03978cc3d Update Zone.lua
Correct radius sphere search in  ZONE_RADIUS:SearchZone()
2025-01-22 09:43:57 +01:00
Frank
3a7233b594 Merge branch 'master' into develop 2025-01-19 19:18:26 +01:00
Frank
82f4c5790a Merge branch 'master' of https://github.com/FlightControl-Master/MOOSE 2025-01-19 19:18:01 +01:00
Frank
5957124e9e ARTY v1.3.3
- Added missing/new arty units to DB (min/max firing range)
- Adjusted immobile speed check because some DCS units report a speed of 1 m/s
2025-01-19 19:17:59 +01:00
Applevangelist
7c91b9847b Merge remote-tracking branch 'origin/master' into develop 2025-01-19 18:23:19 +01:00
Applevangelist
92a05ca74a #CTLD - If troops or vehicles have a stock set, you can only inject as many as there are in stock. Specifically, when using persistence, the load function will restrict to inject more objects than are in stock, each inject draws on the stock. 2025-01-19 18:22:25 +01:00
Applevangelist
f77771ae25 xx 2025-01-19 18:19:54 +01:00
Applevangelist
a3c13c8cea #PLAYERTASK - added option for custom player callsigns
#PLAYERRECCE - added option for custom player callsigns
#PLAYERRECCE - added KIOWA support
2025-01-19 17:41:17 +01:00
Applevangelist
c62dc17cd8 xxx 2025-01-19 17:39:54 +01:00
Frank
d02b5db6dd Merge branch 'master' of https://github.com/FlightControl-Master/MOOSE 2025-01-18 22:34:25 +01:00
Frank
bd074728fe Update Artillery.lua
- Update Arty DB
2025-01-18 22:34:21 +01:00
Applevangelist
8d87531464 #PLAYERRECCE - Add standard FOW for Kiowa 2025-01-18 15:32:08 +01:00
Applevangelist
5fbd3d9525 Merge remote-tracking branch 'origin/master' into develop 2025-01-18 15:30:33 +01:00
Applevangelist
61b7b3ead6 Smaller fixes 2025-01-18 15:30:03 +01:00
Applevangelist
3ed61e8a2a xx 2025-01-18 15:29:35 +01:00
Applevangelist
2a1234f7ff xxx 2025-01-17 12:19:42 +01:00
Applevangelist
8f2178a79c Merge remote-tracking branch 'origin/master' into develop 2025-01-17 09:56:00 +01:00
Applevangelist
72bb23ed0d xx 2025-01-17 09:55:12 +01:00
Applevangelist
7f7999e3e5 #CSAR - Make the list of downed pilots' coordinate dependent on _SETTINGS global or pilot 2025-01-17 09:54:41 +01:00
Applevangelist
77c6365e37 xx 2025-01-17 09:53:16 +01:00
Applevangelist
0a08e3fdac Merge remote-tracking branch 'origin/master' into develop 2025-01-17 09:22:32 +01:00
Applevangelist
b522b38d31 #SCENERY - Some fixes for kissing getLife function on some objects, and life points being zero on some objects 2025-01-17 09:21:48 +01:00
Applevangelist
5c1d367926 xx 2025-01-16 11:10:42 +01:00
Applevangelist
869339150a Merge remote-tracking branch 'origin/develop' into branch
# Conflicts:
#	Moose Development/Moose/Ops/Awacs.lua
2025-01-15 15:36:08 +01:00
Applevangelist
9b5a653940 Merge remote-tracking branch 'origin/master' into branch 2025-01-15 15:35:36 +01:00
Thomas
646a2aec66 Merge pull request #2221 from FlightControl-Master/Applevangelist-awacs-1
Update Awacs.lua
2025-01-15 15:34:36 +01:00
Thomas
7392cb9bf3 Update Awacs.lua
#AWACS - clarify use of escort parameters and template
2025-01-15 12:14:14 +01:00
Frank
2e77988473 Merge branch 'master' into develop 2025-01-14 10:36:27 +01:00
Frank
b2dc7bc232 ARTY v1.3.2
- Fixed bug that MLRS does not fire (ammo count if weapon type is auto)
2025-01-14 10:36:12 +01:00
Applevangelist
083bf13fe4 xx 2025-01-13 18:01:31 +01:00
Applevangelist
1b25220729 #AWACS - make # of escorts required assets 2025-01-13 12:38:53 +01:00
Applevangelist
50436b8ced #AWACS - make # of escorts required assets 2025-01-13 12:38:39 +01:00
Applevangelist
b82e15b2ce Merge remote-tracking branch 'origin/develop' into branch 2025-01-13 11:49:54 +01:00
Applevangelist
4953000565 Merge remote-tracking branch 'origin/develop' into develop 2025-01-13 11:46:56 +01:00
Applevangelist
01b7575bca #AWACS
- Fix a logic error: cancel the Escort mission when the replacement has arrived
- Workaround for a situation where a player has a checkin-entry in the menu and a managed group entry at the same time - which should not be the case.
- Added Escort options for formation and relative position behind the AWACS
2025-01-13 11:46:51 +01:00
Applevangelist
8ffa1bd692 #AWACS
- Fix a logic error: cancel the Escort mission when the replacement has arrived
- Workaround for a situation where a player has a checkin-entry in the menu and a managed group entry at the same time - which should not be the case.
2025-01-13 11:46:07 +01:00
Frank
f6e6dcac9a OPSGROUP ARTY
- Fixed bug in surface type for coord in range
- Improved search for coord in range
- Added option to set mission waypoint
2025-01-12 23:54:44 +01:00
Applevangelist
c855198634 Merge remote-tracking branch 'origin/develop' into branch 2025-01-12 17:12:09 +01:00
Applevangelist
8aa8acdad0 Merge remote-tracking branch 'origin/master' into branch 2025-01-12 17:11:50 +01:00
Applevangelist
93c307d9dd Merge remote-tracking branch 'origin/develop' into develop 2025-01-12 17:10:32 +01:00
Applevangelist
852c18cef8 Merge remote-tracking branch 'origin/master' into develop 2025-01-12 17:10:27 +01:00
Applevangelist
def622a02c #CTLD - InjectTroops - if precision coords is true, do also not randomize unit positions. 2025-01-12 17:09:31 +01:00
Applevangelist
382ad6ecd8 xx 2025-01-12 17:02:40 +01:00
Frank
acfe1a856e Update OpsGroup.lua
- Added surface type check for ARTY mission
- Improved search for coordinate in range
- Added option to specify distance conversion for weapon ranges
2025-01-11 23:38:15 +01:00
Thomas
9f1f6af647 Merge pull request #2220 from FlightControl-Master/master
Merge master
2025-01-11 10:17:39 +01:00
Thomas
30bedb39f1 Merge pull request #2219 from FlightControl-Master/Applevangelist-patch-1
Update Unit.lua
2025-01-11 10:14:44 +01:00
Thomas
fbcc4ee32b Update Unit.lua
GetSTN fix if template os nil
2025-01-11 10:14:03 +01:00
Thomas
27571cc22f Merge pull request #2217 from shaji-Dev/develop
Develop
2025-01-11 10:04:44 +01:00
Shafik
1182a5eb1e [FIXED] attempt to index field 'IniDCSGroup' (a nil value) 2025-01-10 21:40:03 +02:00
Applevangelist
f009370e07 xx 2025-01-09 14:15:09 +01:00
Applevangelist
0e91a658b3 Merge remote-tracking branch 'origin/develop' into branch 2025-01-08 13:06:10 +01:00
Applevangelist
774317f2c5 Merge remote-tracking branch 'origin/master' into branch 2025-01-08 13:06:06 +01:00
Applevangelist
5f57c71c62 Merge remote-tracking branch 'origin/develop' into develop 2025-01-08 13:05:28 +01:00
Applevangelist
c0d60b1bcf Merge remote-tracking branch 'origin/master' into develop 2025-01-08 13:05:24 +01:00
Applevangelist
a33e542df5 xx 2025-01-08 13:04:54 +01:00
Applevangelist
63c68d729b #SET_GROUP - fix for lost event if IniDCSGroup isn't filled 2025-01-08 13:04:30 +01:00
Thomas
5fe29e2ba1 Merge pull request #2214 from FlightControl-Master/Applevangelist-patch-3
Update Auftrag.lua
2025-01-07 09:22:12 +01:00
Thomas
16ff1ac3e7 Update Auftrag.lua
#AUFTRAG Added some options for weapontype setting
2025-01-07 09:21:26 +01:00
Applevangelist
0e4f805e1c Merge remote-tracking branch 'origin/develop' into develop 2025-01-06 17:33:09 +01:00
Applevangelist
5f29bdc7a7 Merge remote-tracking branch 'origin/master' into develop 2025-01-06 17:33:04 +01:00
Applevangelist
04f8f6d512 xx 2025-01-06 17:32:29 +01:00
Applevangelist
6adb9f0839 xx 2025-01-06 17:31:03 +01:00
Thomas
f4768aff07 Merge pull request #2213 from FlightControl-Master/master
Master merge
2025-01-06 14:21:42 +01:00
Thomas
b74d46f762 Merge pull request #2211 from FlightControl-Master/Applevangelist-spawn-1
Update Spawn.lua
2025-01-06 14:20:23 +01:00
Thomas
02decc3901 Merge pull request #2212 from Rolln-dev/rolln/range
#RANGE - Changed parameter naming to fix docs
2025-01-06 14:19:02 +01:00
Rolln
dcdea16624 #RANGE - Changed parameter naming to fix docs 2025-01-06 06:15:35 -07:00
Thomas
2635cf6345 Update Spawn.lua
#SPAWN Added option to use the center of the zone rather than a random postion when using InitRandomizeZone
2025-01-06 14:08:05 +01:00
Applevangelist
44a433eadf Merge remote-tracking branch 'origin/develop' into branch 2025-01-05 17:45:54 +01:00
Applevangelist
40cd58aa1c Merge remote-tracking branch 'origin/master' into branch 2025-01-05 17:45:47 +01:00
Applevangelist
578c65196c Merge remote-tracking branch 'origin/master' into develop 2025-01-05 17:45:08 +01:00
Applevangelist
64fb24ce96 Merge remote-tracking branch 'origin/develop' into develop 2025-01-05 17:44:37 +01:00
Applevangelist
45dd7117c7 #CLIENTMENUMANAGER Add a retry loop if possibly client not yet exists in Database 2025-01-05 17:44:32 +01:00
Applevangelist
a2c241da43 xx 2025-01-05 17:43:21 +01:00
Applevangelist
e86069d39c Smaller updates 2025-01-05 17:43:12 +01:00
Thomas
e9db714937 Merge pull request #2210 from FlightControl-Master/master
Master merhe
2025-01-05 10:21:40 +01:00
Thomas
fdcf153b0b Merge pull request #2209 from shaji-Dev/master
[FIXED] `attempt to call method 'GetPlayerName' (a nil value)`
2025-01-05 10:20:23 +01:00
Shafik
8523b7e20a [FIXED] attempt to call method 'GetPlayerName' (a nil value) 2025-01-05 10:47:14 +02:00
Frank
29b992bf81 Merge pull request #2208 from Rolln-dev/rolln/range
ENHANCEMENT: Adds a ceiling to the range.
2025-01-04 19:15:33 +01:00
Rolln
4c52509d6d ENHANCEMENT: Adds a ceiling to the range. 2025-01-04 11:06:39 -07:00
Applevangelist
34a11e79b3 Merge remote-tracking branch 'origin/develop' into branch 2025-01-04 18:28:06 +01:00
Applevangelist
1e4d2f4427 Merge remote-tracking branch 'origin/master' into branch 2025-01-04 18:27:59 +01:00
Applevangelist
8d603a0cef Merge remote-tracking branch 'origin/master' into develop 2025-01-04 18:26:32 +01:00
Applevangelist
9bc067f2e8 #STORAGE - Switched saving items from the storage inventory to using the enumerator (+~120 items), better handling of table IDs. 2025-01-04 18:25:32 +01:00
Applevangelist
5fbe0d9a70 #ENUMS - Added items to the storage enumerator, some number corrections 2025-01-04 18:23:24 +01:00
Applevangelist
a3d7c9e537 xx 2025-01-04 18:22:36 +01:00
Shafik
4c1c36ef46 [FIXED] attempt to call method 'GetPlayerName' (a nil value) 2025-01-04 16:16:03 +02:00
Applevangelist
9966c5eadf Merge remote-tracking branch 'origin/develop' into branch 2025-01-04 13:26:43 +01:00
Applevangelist
66bd771662 Merge remote-tracking branch 'origin/master' into branch 2025-01-04 13:26:34 +01:00
Applevangelist
067fbcaeaf Merge remote-tracking branch 'origin/develop' into develop 2025-01-04 13:25:38 +01:00
Applevangelist
f0aad83d53 Merge remote-tracking branch 'origin/master' into develop 2025-01-04 13:25:33 +01:00
Applevangelist
474f767e56 #CTLD - Clarify doc for Hook loadable stuff 2025-01-04 13:24:51 +01:00
Applevangelist
deb0431f93 #CTLD - Clarify doc for Hook loadable stuff 2025-01-04 13:24:40 +01:00
Frank
be062061d5 Update Auftrag.lua
- Improved docs for ARTY
2025-01-02 23:01:49 +01:00
Applevangelist
5cc3fd7238 xx 2025-01-02 17:23:38 +01:00
Applevangelist
ac5dfab82f #PLAYERRECCE - Fine tune check when a unit is dead or just per se has less than 2 LifePoints 2025-01-02 17:22:39 +01:00
Applevangelist
13d5f4ac99 Merge remote-tracking branch 'origin/master' into develop 2025-01-02 17:07:29 +01:00
Applevangelist
f9030be843 xx 2025-01-02 17:06:53 +01:00
Applevangelist
c1c5467b9d xx 2025-01-02 17:06:42 +01:00
Applevangelist
3cea1cc78c #PLAYERRECCE - allow to display add'l location in report as per user settings for laser and visual targets. 2025-01-02 16:20:47 +01:00
Applevangelist
f67706044a #PLAYERRECCE - allow add'l position to be displayed 2025-01-02 15:46:42 +01:00
Applevangelist
37c1423c9f Merge remote-tracking branch 'origin/master' into develop 2025-01-02 13:20:55 +01:00
Applevangelist
538e35d8f0 xx 2025-01-02 13:20:18 +01:00
Applevangelist
2cb58bd351 xx 2025-01-02 13:15:13 +01:00
Applevangelist
fc8c1c156f Merge remote-tracking branch 'origin/develop' into branch 2025-01-02 11:20:53 +01:00
Applevangelist
55052f4451 Merge remote-tracking branch 'origin/master' into branch 2025-01-02 11:20:45 +01:00
Applevangelist
12dd4a918f xx 2025-01-02 11:20:10 +01:00
Applevangelist
d23f029953 Merge remote-tracking branch 'origin/master' into develop 2025-01-02 11:19:39 +01:00
Applevangelist
aafa37f80d Merge remote-tracking branch 'origin/develop' into develop 2025-01-02 11:19:16 +01:00
Applevangelist
203f0c8abc #docu 2025-01-02 11:18:44 +01:00
Thomas
1c0e6362a1 Merge pull request #2205 from shaji-Dev/Heartbreaker
[FIXED] OpsZone never goes to `Attacked` state if `threatlevelCapture` is higher than 0.
2025-01-02 07:31:11 +01:00
Shafik
68165cee75 [FIXED] OpsZone never goes to Attacked state if threatlevelCapture is higher than 0. 2025-01-01 23:08:30 +02:00
Applevangelist
8ef5f0b3f6 Merge remote-tracking branch 'origin/master' into develop 2025-01-01 17:56:35 +01:00
Applevangelist
d0736b0b56 #SHORAD - Switch shorad group back to green&no emissions&scoot if it's directly attacked by a missile 2025-01-01 17:55:55 +01:00
Applevangelist
21a6bcfcfb xx 2025-01-01 17:54:59 +01:00
Applevangelist
2fbcd9d2b9 Merge remote-tracking branch 'origin/master' into develop 2025-01-01 14:38:43 +01:00
Applevangelist
15b1ed028e #MANTIS - better handling of stats, verbose dist calc in km, coord in MGRS0 now 2025-01-01 14:38:07 +01:00
Applevangelist
dc9b70db7e xx 2025-01-01 14:37:28 +01:00
Applevangelist
3b03b03f3c Merge remote-tracking branch 'origin/develop' into branch 2025-01-01 09:15:50 +01:00
Applevangelist
3923d81c87 Merge remote-tracking branch 'origin/master' into branch 2025-01-01 09:15:42 +01:00
Applevangelist
a611a38181 Merge remote-tracking branch 'origin/master' into develop 2025-01-01 09:14:46 +01:00
Applevangelist
008617a35c #MANTIS - Fix for Checkloop if no Sharad instance is alive 2025-01-01 09:14:07 +01:00
Thomas
81a403f02d Merge pull request #2204 from FlightControl-Master/master
Update Mantis.lua
2025-01-01 08:32:57 +01:00
Thomas
6144a61a2e Update Mantis.lua
Fix for zones count on possible empty variables
2025-01-01 08:31:06 +01:00
Applevangelist
3090100265 Merge remote-tracking branch 'origin/develop' into branch 2024-12-31 15:36:14 +01:00
Applevangelist
87356291f7 Merge remote-tracking branch 'origin/master' into branch 2024-12-31 15:36:05 +01:00
Applevangelist
df102fba6c Merge remote-tracking branch 'origin/master' into develop 2024-12-31 15:35:23 +01:00
Applevangelist
5d192abd25 #SEAD - Add AGM_65 2024-12-31 15:34:46 +01:00
Applevangelist
633186ca8a xx 2024-12-31 15:34:28 +01:00
Applevangelist
411d20ba26 #SEAD - Add AGM_65 2024-12-31 15:33:28 +01:00
Applevangelist
902f6dae11 Merge remote-tracking branch 'origin/master' into develop 2024-12-31 14:05:41 +01:00
Applevangelist
62337f445a #CTLD - Save and load special static shapes, if they are set 2024-12-31 14:04:46 +01:00
Applevangelist
6eac89c038 #CTLD - also save and load static special shapes if they are set 2024-12-31 14:04:16 +01:00
Applevangelist
08b6fa520d Merge remote-tracking branch 'origin/develop' into branch 2024-12-30 14:43:02 +01:00
Applevangelist
b69cea999e Merge remote-tracking branch 'origin/master' into branch 2024-12-30 14:42:56 +01:00
Applevangelist
b38c1c5827 xx 2024-12-30 14:42:03 +01:00
Thomas
8309768735 Merge pull request #2203 from shaji-Dev/Heartbreaker
Mankind saved.
2024-12-30 13:40:54 +01:00
Shafik
42e6b57296 Mankind saved. 2024-12-30 14:09:25 +02:00
Shafik
2835ec812f Mankind saved. 2024-12-30 14:08:04 +02:00
Applevangelist
91db9127da Merge remote-tracking branch 'origin/develop' into branch 2024-12-30 12:05:17 +01:00
Applevangelist
c4512651cf Merge remote-tracking branch 'origin/master' into branch 2024-12-30 12:05:10 +01:00
Applevangelist
990161729b Merge remote-tracking branch 'origin/master' into develop 2024-12-30 11:47:11 +01:00
Applevangelist
c08cee2317 #STORAGE Documentation for persistence 2024-12-30 11:45:31 +01:00
Applevangelist
26530f25fb Merge remote-tracking branch 'origin/develop' into branch 2024-12-29 18:10:12 +01:00
Applevangelist
d51961ac8c Merge remote-tracking branch 'origin/master' into branch
# Conflicts:
#	Moose Development/Moose/Wrapper/Storage.lua
2024-12-29 18:10:05 +01:00
Applevangelist
e1ea4322a1 Merge remote-tracking branch 'origin/master' into develop 2024-12-29 18:08:45 +01:00
Applevangelist
0f7759d070 #STORAGE - Added StartAutoSave and StopAutoSave() 2024-12-29 18:04:42 +01:00
Applevangelist
6523c4473f Merge remote-tracking branch 'origin/master' into develop 2024-12-29 12:53:35 +01:00
Applevangelist
4a0842bea6 #STORAGE - Added persistence options 2024-12-29 12:52:51 +01:00
Applevangelist
80e20e59ec #STORAGE - Added persistence options 2024-12-29 12:52:21 +01:00
Applevangelist
80906ded28 Merge remote-tracking branch 'origin/develop' into branch 2024-12-28 14:53:42 +01:00
Applevangelist
7cb04e65ca Merge remote-tracking branch 'origin/master' into branch 2024-12-28 14:53:38 +01:00
Applevangelist
3745e6a8d8 Merge remote-tracking branch 'origin/master' into develop 2024-12-28 14:52:54 +01:00
Applevangelist
3b3666c5f7 #AIRBASE add 2 Kola bases, correct #ENUM for OH-58D weps in STORAGE 2024-12-28 14:52:09 +01:00
Thomas
9e1a28f7ca Merge pull request #2202 from Fedge7/personal/fedge/enums-weapons-oh58-dec2024
Corrects the table values for OH58 weapon enums for patch `2.9.11.4686.`
2024-12-28 09:26:46 +01:00
Fedge
82c7e921eb Corrects the table values for OH58 weapon enums for patch 2.9.11.4686. 2024-12-27 18:48:30 -07:00
Applevangelist
67ccf532ea Merge remote-tracking branch 'origin/master' into develop 2024-12-27 14:41:15 +01:00
Applevangelist
45912911ee #MSRS Google 2025 voice catalog 2024-12-27 14:40:32 +01:00
Applevangelist
8cd625b6f9 #MSRS Google 2025 voice catalog 2024-12-27 14:40:23 +01:00
Applevangelist
152a8cf9da Merge remote-tracking branch 'origin/develop' into branch 2024-12-27 12:27:42 +01:00
Applevangelist
bb648b4396 Merge remote-tracking branch 'origin/master' into branch 2024-12-27 12:27:21 +01:00
Applevangelist
c7fe42d919 Merge remote-tracking branch 'origin/master' into develop 2024-12-27 12:26:31 +01:00
Applevangelist
9916afaa49 #COORDINATE Adding names to smoke to be able to switch them off earlier 2024-12-27 12:25:25 +01:00
Applevangelist
55c5a23616 #CTLD Bugfix in selecting correct helo distance when hover/ground for the Chinook 2024-12-27 12:25:00 +01:00
Applevangelist
7b6df08268 #AUTOLASE - Somewhat nicer report display 2024-12-27 12:24:26 +01:00
Applevangelist
ecdca9919e xx 2024-12-27 12:24:02 +01:00
Applevangelist
9bf2a013d5 Merge remote-tracking branch 'origin/develop' into branch 2024-12-26 17:20:56 +01:00
Applevangelist
4b2bc909e6 Merge remote-tracking branch 'origin/master' into branch 2024-12-26 17:20:51 +01:00
Applevangelist
b3ed0681a8 xx 2024-12-26 17:19:14 +01:00
Applevangelist
9300050573 Merge remote-tracking branch 'origin/master' into develop 2024-12-26 17:18:28 +01:00
Applevangelist
6bee1cc88e Small additions 2024-12-26 17:16:43 +01:00
Applevangelist
0e75f64217 Merge remote-tracking branch 'origin/develop' into branch 2024-12-22 13:01:34 +01:00
Applevangelist
c94767a715 Merge remote-tracking branch 'origin/develop' into develop 2024-12-22 13:00:49 +01:00
Applevangelist
7aa3169523 #AWACS - add a mission parameter to ensure selection of AWACS aircraft 2024-12-22 13:00:46 +01:00
Applevangelist
678dc92a66 #AWACS - add a mission parameter to ensure selection of AWACS aircraft 2024-12-22 13:00:22 +01:00
Applevangelist
7f323dd3a5 Merge remote-tracking branch 'origin/develop' into branch 2024-12-21 11:58:22 +01:00
Applevangelist
6ebd36e0b5 Merge remote-tracking branch 'origin/master' into branch 2024-12-21 11:58:19 +01:00
Applevangelist
f7583dbd56 x 2024-12-21 11:22:52 +01:00
Applevangelist
d0aa039626 xx 2024-12-19 14:16:10 +01:00
Frank
cf86eacb16 Merge branch 'master' into develop 2024-12-18 22:04:55 +01:00
Frank
43ab4d5f38 Update Warehouse.lua
- Removed trace output of qitem and queue because it stalls the game when written to log file
2024-12-18 22:04:38 +01:00
Applevangelist
844ad4619e Merge remote-tracking branch 'origin/develop' into branch 2024-12-18 12:36:16 +01:00
Applevangelist
a73a12f73b Merge remote-tracking branch 'origin/master' into branch 2024-12-18 12:36:09 +01:00
Applevangelist
aa064a1d0e Merge remote-tracking branch 'origin/master' into develop 2024-12-18 12:35:32 +01:00
Applevangelist
21240a60de #AUFTRAG, OpsGroup - added speed option for mission hold, ingress and egress wpts 2024-12-18 12:35:13 +01:00
Applevangelist
0427c0d3a7 #CTLD - Added GetLoadedCargo(Unit) 2024-12-18 12:34:30 +01:00
Applevangelist
925754f290 xx 2024-12-18 12:34:00 +01:00
Applevangelist
3efe02fe37 xx 2024-12-18 11:36:36 +01:00
Applevangelist
3f632b92e2 Merge remote-tracking branch 'origin/master' into develop 2024-12-18 11:35:39 +01:00
Applevangelist
014750ea7f #SET - Documentation fixes 2024-12-18 11:35:13 +01:00
Applevangelist
84b1acc9d5 Merge remote-tracking branch 'origin/develop' into branch 2024-12-17 12:47:48 +01:00
Applevangelist
ee144620ef Merge remote-tracking branch 'origin/master' into branch 2024-12-17 12:47:36 +01:00
Applevangelist
548b80969a Merge remote-tracking branch 'origin/master' into develop 2024-12-17 12:46:44 +01:00
Applevangelist
e4bbfce314 #MANTIS
- Better status overview
- Some fixes for SHORAD setup
2024-12-17 12:45:45 +01:00
Applevangelist
f8808e0231 xx 2024-12-17 12:43:09 +01:00
Applevangelist
3b02e153ba xx 2024-12-17 12:38:57 +01:00
Applevangelist
278f1db9a9 Merge remote-tracking branch 'origin/master' into develop 2024-12-15 13:35:03 +01:00
Applevangelist
359429b17e #GROUP added Teleport(Coordinate) function leveraging Respawn(). Now also translate routes if there are any. 2024-12-15 13:34:13 +01:00
Applevangelist
87a94a72a6 #GROUP added Teleport(Coordinate) function leveraging Respawn(). Now also translate routes if there are any. 2024-12-15 13:33:18 +01:00
Applevangelist
de8d356e6a Merge remote-tracking branch 'origin/develop' into branch 2024-12-15 11:42:41 +01:00
Applevangelist
58f5152fe5 Merge remote-tracking branch 'origin/master' into branch 2024-12-15 11:42:35 +01:00
Applevangelist
f151271cb1 Merge remote-tracking branch 'origin/master' into develop 2024-12-15 11:42:04 +01:00
Applevangelist
6001f6abda #EVENT - another fix for scenery without getName function 2024-12-15 11:41:24 +01:00
Applevangelist
5b5bd83d5a Merge remote-tracking branch 'origin/develop' into branch 2024-12-15 11:34:17 +01:00
Applevangelist
90c78f304c Merge remote-tracking branch 'origin/master' into branch 2024-12-15 11:34:08 +01:00
Applevangelist
18e5012546 Merge remote-tracking branch 'origin/master' into develop 2024-12-15 11:33:35 +01:00
Applevangelist
fd9b5d8d16 #AIRBASE type fix for single Helipad 2024-12-15 11:33:01 +01:00
Applevangelist
6e2a058e6e Merge remote-tracking branch 'origin/develop' into branch 2024-12-15 11:30:01 +01:00
Applevangelist
3c878be568 Merge remote-tracking branch 'origin/master' into branch 2024-12-15 11:29:51 +01:00
Frank
da3ee13582 Merge branch 'master' into develop 2024-12-15 10:01:13 +01:00
Frank
b6f184388a ATIS
- Fixed length of niner sound file
2024-12-15 10:00:59 +01:00
Frank
903c065b74 Merge branch 'master' into develop 2024-12-14 16:55:36 +01:00
Frank
d8471698ab Merge pull request #2197 from FlightControl-Master/FF/MasterDevel
SPAWNSTATIC
2024-12-14 16:54:50 +01:00
Frank
6204cecbbd SPAWNSTATIC
- moved AddStatic to after static spawn fixing registration of spawned static
2024-12-14 16:54:39 +01:00
Frank
ddeca49916 Merge pull request #2196 from FlightControl-Master/FF/MasterDevel
DATABASE (fix for SPAWNSTATIC)
2024-12-14 13:48:40 +01:00
Frank
d8dcf37886 DATABASE
- Fixed error that static was not returned in `DATABASE:AddStatic()` (leads to issues if a static object with the same name is spawned)
- Other minor changes
2024-12-14 13:48:04 +01:00
Frank
5ae41a208b Merge branch 'master' into FF/MasterDevel 2024-12-14 11:29:57 +01:00
Applevangelist
0462d900e6 xx 2024-12-13 12:08:54 +01:00
Applevangelist
4fb2ad88bc #OpsGroup - fix holding condition for FG at holding coordinate 2024-12-13 12:07:31 +01:00
Applevangelist
465f8a5cc9 xx 2024-12-13 12:05:18 +01:00
Applevangelist
7dd67bdf0e Merge remote-tracking branch 'origin/develop' into branch 2024-12-12 13:15:40 +01:00
Applevangelist
e9e0f82932 Merge remote-tracking branch 'origin/master' into branch 2024-12-12 13:14:46 +01:00
Applevangelist
ea55e90e62 Merge remote-tracking branch 'origin/develop' into develop 2024-12-12 13:11:58 +01:00
Applevangelist
6025c05f33 #AUFTRAG #OPSGROUP Handling of Ingress and Holding points for FlightGroups 2024-12-12 13:11:54 +01:00
Applevangelist
bb43a0e03c #AUFTRAG #OPSGROUP - Flightgroup handling for ingress and holding coord 2024-12-12 13:07:50 +01:00
Frank
64f854e646 Merge branch 'master' into develop 2024-12-12 00:22:59 +01:00
Frank
0f962461e1 DCS Iraq update
- `AIRBASE`: Added Iraq airports to enums
- `ATIS`: Added missing maps to enums
- `UTILS`: Added missing maps to enums
2024-12-12 00:22:44 +01:00
Frank
62dfb5b5ff OPS update
- Set destination base to airwing base for flight groups
- Restored setting of home, destination and current base in OPSGROUP
- Fixed bug if no airbase associated with airwing in check rescuehelo
- Added check that cohort names are unique
- Removed InitWaypoints function from FLIGHTGROUP (obsolete)
2024-12-11 22:49:40 +01:00
Applevangelist
171af5a3c3 #AUFTRAG, #OPSGROUP - Better functionality for Ingress Points (used as IP Holding Point when combined with a push condition) 2024-12-11 16:45:05 +01:00
Applevangelist
3805ab226b #AUFTRAG Better use of Ingress Coordinate 2024-12-11 16:41:22 +01:00
Applevangelist
f1445cdbdd # 2024-12-11 14:36:54 +01:00
Applevangelist
874548d1c1 Merge remote-tracking branch 'origin/master' into develop 2024-12-11 14:18:51 +01:00
Applevangelist
aadc03c38d #CTLD Added option/value for my_ctld.TroopUnloadDistHoverHook 2024-12-11 14:17:58 +01:00
Applevangelist
c80f128b0f #CTLD added 2024-12-11 14:17:14 +01:00
Applevangelist
ef78549af0 Merge remote-tracking branch 'origin/develop' into branch 2024-12-11 13:59:02 +01:00
Applevangelist
5b437d6fb1 Merge remote-tracking branch 'origin/master' into branch 2024-12-11 13:58:55 +01:00
Applevangelist
fb0aeafaa2 Merge remote-tracking branch 'origin/master' into develop 2024-12-11 13:58:23 +01:00
Applevangelist
683fa13bb2 #WEAPON - fix for name not available on target 2024-12-11 13:57:55 +01:00
Applevangelist
d3bd55a290 Merge remote-tracking branch 'origin/master' into develop 2024-12-11 13:54:33 +01:00
Applevangelist
e4408a964d #MANTIS - update CHM assets 2024-12-11 13:54:01 +01:00
Applevangelist
3a3a3baf93 xx 2024-12-11 13:53:30 +01:00
Applevangelist
29efd4c4fb Merge remote-tracking branch 'origin/develop' into branch 2024-12-11 13:42:33 +01:00
Applevangelist
1a9827ec31 Merge remote-tracking branch 'origin/Apple/Develop' into branch 2024-12-11 13:41:47 +01:00
Applevangelist
4ae5e4ddd0 xx 2024-12-11 13:41:44 +01:00
Frank
1fbe78b667 Merge pull request #2195 from FlightControl-Master/FF/Ops
OPSGROUP v1.0.3
2024-12-10 22:50:30 +01:00
Frank
4902e0f597 OPSGROUP v1.0.3
- fixes for (naval) teleporting
2024-12-10 22:44:44 +01:00
Thomas
270eeac084 Update Auftrag.lua
Ingress small typo fic
2024-12-10 19:30:59 +01:00
Thomas
34e248b1c3 Merge pull request #2194 from shaji-Dev/Heartbreaker
[Fixed] FlightGroup do not RTB because of incorrect `destbase` assignment.
2024-12-10 19:08:42 +01:00
shaji
5a05917bc2 [Fixed] FlightGroup do not RTB because of incorrect destbase assignment. 2024-12-10 19:04:09 +01:00
Applevangelist
eca130b3af #AUFTRAG/OPS allow extra ingress coord for air groups 2024-12-10 12:12:56 +01:00
Frank
98a613261c Update OpsGroup.lua
- Delayed spawn
2024-12-09 23:32:24 +01:00
Frank
ee880a893e OPSGROUP
- Improved respawn behaviour
2024-12-08 22:47:16 +01:00
Applevangelist
426297812e xx 2024-12-08 18:56:48 +01:00
Applevangelist
7e8555d6b7 #EASYGCICAP - allow multiple EWR string prefixes in a table 2024-12-08 18:56:28 +01:00
Applevangelist
0a6bdd6e04 #SET_OPSGROUP - fix for teleport/respawn 2024-12-06 14:07:09 +01:00
Applevangelist
855ea764b1 #SET_OPSGROUP - fix for teleport/respawn 2024-12-06 14:06:56 +01:00
Frank
f97f33ab59 ATIS v1.1.0
- Added dynamic fog
2024-12-06 00:09:15 +01:00
Frank
f59102ee09 Merge branch 'master' into FF/MasterDevel 2024-12-05 21:06:06 +01:00
Applevangelist
e53f1aa68d Merge remote-tracking branch 'origin/develop' into branch
# Conflicts:
#	Moose Development/Moose/Functional/Mantis.lua
#	Moose Development/Moose/Ops/Auftrag.lua
2024-12-05 10:59:08 +01:00
Applevangelist
7c06c615d0 xx 2024-12-05 10:56:54 +01:00
Applevangelist
de304d6bb9 Merge remote-tracking branch 'origin/develop' into develop 2024-12-05 10:56:31 +01:00
Applevangelist
73181e3f45 xx 2024-12-05 10:56:27 +01:00
Frank
e42e4e1ddf Merge branch 'master' into FF/MasterDevel 2024-12-03 21:15:46 +01:00
Thomas
86e899f39b Merge pull request #2192 from FlightControl-Master/master
Update Mantis.lua
2024-12-03 18:10:06 +01:00
Thomas
a30079c45b Update Mantis.lua
Less noise
2024-12-03 18:07:38 +01:00
Frank
f644d49e71 OPS Speed
- `AUFTRAG` Fixed speed unit conversion in multiple AUFTRAG types (DCS task parameter speed should be in m/s)
- `OPSGROUP`: Fixed unit conversion for engage target speed
2024-12-01 14:56:02 +01:00
Applevangelist
c0b09b03d9 Merge remote-tracking branch 'origin/master' into develop 2024-11-30 16:20:14 +01:00
Applevangelist
50ffd9aba6 #MANTIS - Correct detail data check is run even if automode isn't defined 2024-11-30 16:19:51 +01:00
Applevangelist
bc60648458 xx 2024-11-30 16:19:21 +01:00
Applevangelist
116527abfe xx 2024-11-30 13:09:58 +01:00
Applevangelist
a0f58a95db xx 2024-11-30 12:55:50 +01:00
Applevangelist
c55732a02f Leaner new version 2024-11-30 12:49:36 +01:00
Applevangelist
4d4138c1e6 Merge remote-tracking branch 'origin/master' into develop 2024-11-28 09:47:43 +01:00
Frank
936fec1f49 Update Range.lua 2024-11-26 23:06:31 +01:00
Applevangelist
9625d87dd5 #NET - fix for net.force_player_slot(PlayerID, SideID, SlotID ) not working at all - destroy blocked client instead 2024-11-26 10:43:20 +01:00
Applevangelist
25740f7e61 xx 2024-11-26 10:40:46 +01:00
Applevangelist
66b6e4475e Merge remote-tracking branch 'origin/develop' into branch 2024-11-26 08:07:10 +01:00
Applevangelist
76a8bc7a33 Merge remote-tracking branch 'origin/master' into branch 2024-11-26 08:06:48 +01:00
Applevangelist
633f990b36 xxx 2024-11-26 08:05:40 +01:00
Frank
c4d0319bb2 Merge branch 'master' into develop 2024-11-24 21:54:19 +01:00
Frank
802139205c Update Unit.lua
- Fixed bug in `UNIT:GetDCSObject()` when DCS unit does not exist.
2024-11-24 21:54:05 +01:00
Frank
6022a3905f TARGET v0.7.1
- Fixed bug if TARGET is a COORDINATE or ZONE, where we cannot check if it is alive or dead. The count alive previously did not count coordinates or zones. Now it does, which is more consistent as we set life=1 and alive=true for these.
2024-11-23 22:10:26 +01:00
Frank
6f724c62fb Merge branch 'master' into develop 2024-11-21 23:44:13 +01:00
Frank
fb918cb2a4 Update DCS.lua 2024-11-21 23:43:58 +01:00
Frank
1a74f112ef Merge branch 'master' into develop 2024-11-21 23:13:18 +01:00
Frank
abb4de46d7 Controllable Detection Improvements/Fixes
- Fixed bug in detection functions for optical detection (enum incorrect)
- Fixed bug in order of return values of `CONTROLLABLE:IsTargetDetected` (both hoggit and DCS are WRONG and MOOSE adopted it).
2024-11-21 23:13:05 +01:00
Frank
5747c49abf Merge branch 'develop' of https://github.com/FlightControl-Master/MOOSE into develop 2024-11-20 11:15:09 +01:00
Frank
b87930738a TARGET v0.7
- Bug fixes
- Improved FSM function docs
2024-11-20 11:15:05 +01:00
Applevangelist
4511f79609 Merge remote-tracking branch 'origin/develop' into branch 2024-11-19 08:44:47 +01:00
Thomas
d2c78516f5 Merge pull request #2191 from shaji-Dev/Heartbreaker
[Added] OPSZONE:SetDrawZoneForCoalition
2024-11-19 06:44:13 +01:00
shaji
93cff96794 [Added] OPSZONE:SetDrawZoneForCoalition to set if zone is drawn on the F10 map for the owner coalition only instead for all. Useful for recon tasking and fog of war. 2024-11-19 00:42:37 +01:00
Thomas
75e80a0091 Merge pull request #2190 from shaji-Dev/Heartbreaker
Added usage docs
2024-11-18 18:36:47 +01:00
shaji
46d2c9c196 [Added] Usage to doc for PLAYERTASK:AddStaticObjectSuccessCondition, PLAYERTASK:AddOpsZoneCaptureSuccessCondition, PLAYERTASK:AddReconSuccessCondition, PLAYERTASK:AddTimeLimitFailureCondition 2024-11-18 09:25:00 +01:00
Applevangelist
6f61d160af Documentation 2024-11-18 08:54:21 +01:00
Applevangelist
7ba360a005 Documentation 2024-11-18 08:54:10 +01:00
Applevangelist
c4faf39270 Merge remote-tracking branch 'origin/develop' into branch 2024-11-18 08:24:36 +01:00
Thomas
13419171a9 Merge pull request #2189 from shaji-Dev/Heartbreaker
New New Capture OpsZone PlayerTask and Time Limit Conditions
2024-11-18 06:17:00 +01:00
shaji
147cfea587 [Added] New Capture OpsZone PlayerTask with OPSZONE and SET_OPSZONE targets
[Added] Task "Time Limit" failure condition `PLAYERTASK:AddTimeLimitFailureCondition(TimeLimit)`
[Added] `PLAYERTASK:AddOpsZoneCaptureSuccessCondition(CaptureSquadGroupNamePrefix, Coalition)`
[Added] `PLAYERTASK:_CheckCaptureOpsZoneSuccess`
2024-11-18 02:35:05 +01:00
Applevangelist
1b7dece0a5 Merge remote-tracking branch 'origin/develop' into branch 2024-11-17 16:24:12 +01:00
Applevangelist
ddcc20cc47 Merge remote-tracking branch 'origin/master' into branch 2024-11-17 16:24:07 +01:00
Applevangelist
c27da8e54e Merge remote-tracking branch 'origin/develop' into develop 2024-11-17 16:23:10 +01:00
Applevangelist
f3c8b4c1cd Merge remote-tracking branch 'origin/master' into develop 2024-11-17 16:23:06 +01:00
Applevangelist
cea3ab291e xxx 2024-11-17 16:22:32 +01:00
Frank
09e1883488 Merge branch 'master' into develop 2024-11-15 23:22:00 +01:00
Frank
dce9631399 Update Event.lua
- Set `Event.IniGroupName` from db in case group does not exist any more
2024-11-15 23:21:50 +01:00
Thomas
fb2ba1dea0 Merge pull request #2188 from shaji-Dev/Heartbreaker
Docs update PLAYERTASK:AddReconSuccessCondition
2024-11-11 12:19:49 +01:00
Shafik
19073534dd Docs update for:
PLAYERTASK:AddReconSuccessCondition
2024-11-11 12:45:25 +02:00
Applevangelist
112a0b2a8e Merge remote-tracking branch 'origin/develop' into branch 2024-11-10 15:37:14 +01:00
Applevangelist
1f4ca22018 Merge remote-tracking branch 'origin/master' into branch 2024-11-10 15:37:01 +01:00
Thomas
23080e3cc4 Merge pull request #2187 from shaji-Dev/Heartbreaker
PLAYERTASK Added helper functions
2024-11-10 14:56:24 +01:00
Shafik
b50d3fa809 [Added] Constructor PLAYERTASK:NewFromTarget that determines task type based on the target object
[Added] `PLAYERTASK:_GetTaskTypeForTarget` to get the task type based on target description
[Added] `PLAYERTASKCONTROLLER.Scores` for RECON, ESCORT and CAP
[Added] `PLAYERTASK:AddStaticObjectSuccessCondition` task success condition for dead STATIC, SET_STATIC, SCENERY or SET_SCENERY targets
[Added] `PLAYERTASK:AddReconSuccessCondition` for AUFTRAG.Type.RECON tasks for when a client is at a certain LOS distance from the target
2024-11-10 15:41:21 +02:00
Shafik
1f191cf9bb [Added] Constructor PLAYERTASK:NewFromTarget that determines task type based on the target object
[Added] `PLAYERTASK:_GetTaskTypeForTarget` to get the task type based on target description
[Added] `PLAYERTASKCONTROLLER.Scores` for RECON, ESCORT and CAP
[Added] `PLAYERTASK:AddStaticObjectSuccessCondition` task success condition for dead STATIC, SET_STATIC, SCENERY or SET_SCENERY targets
[Added] `PLAYERTASK:AddReconSuccessCondition` for AUFTRAG.Type.RECON tasks for when a client is at a certain LOS distance from the target
2024-11-10 15:37:16 +02:00
Thomas
6c773786d2 Merge pull request #2186 from FlightControl-Master/Applevangelist-patch-2
Update Enums.lua
2024-11-10 13:46:00 +01:00
Thomas
8939963187 Update Enums.lua
OH58d weapons
2024-11-10 13:45:44 +01:00
Thomas
e3fb693cb7 Merge pull request #2185 from Fedge7/personal/fedge/enums-weapons-oh58
Corrects the table values for OH58 weapon enums.
2024-11-10 13:44:01 +01:00
Fedge
7cc11f55bb Corrects the table values for OH58 weapon enums.
After seeing FARP rearming broken for the OH58 in a recent DCS update, I checked all these enum values and updated them by referencing a freshly-created MIZ. I dropped a FARP and added a unique quantity of all the OH58's weapons to the FARP's warehouse. The values were then scraped from the MIZ's `warehouse` lua file.
2024-11-08 00:09:18 -07:00
Applevangelist
954190f6df xx 2024-11-07 09:05:53 +01:00
Thomas
f9747d1c4c Merge pull request #2184 from FlightControl-Master/Applevangelist-pwsh-1
Update SRS.lua
2024-11-04 14:49:56 +01:00
Thomas
60a3d3409e Update SRS.lua
#MSRS Test to get the pwsh window to minimize
2024-11-04 14:49:26 +01:00
Applevangelist
cd8cbc54c8 Merge remote-tracking branch 'origin/master' into develop 2024-10-31 18:16:32 +01:00
Applevangelist
b72124c0d9 #CONTROLLABLE Corrected parameter omissions 2024-10-31 18:16:02 +01:00
Applevangelist
1307e53833 xx 2024-10-31 18:15:23 +01:00
Applevangelist
0d6a1644e2 Merge remote-tracking branch 'origin/master' into develop 2024-10-31 16:06:00 +01:00
Applevangelist
d51e761b26 #CONTROLLABLE - Rollback changes for EPLRS command 2024-10-31 16:05:24 +01:00
Applevangelist
e45d22ad35 xx 2024-10-31 16:05:03 +01:00
Applevangelist
48dcca4b7b Merge remote-tracking branch 'origin/develop' into branch 2024-10-30 17:11:57 +01:00
Applevangelist
4e708563eb Merge remote-tracking branch 'origin/master' into branch 2024-10-30 17:11:49 +01:00
Applevangelist
1ebe8b82ec Merge remote-tracking branch 'origin/master' into develop 2024-10-30 17:10:25 +01:00
Applevangelist
1445ef61a0 #AIRBASE - added bases from 2.9.9.2280 2024-10-30 17:09:38 +01:00
Applevangelist
f82490f0d5 Merge remote-tracking branch 'origin/master' into develop 2024-10-29 13:57:56 +01:00
Applevangelist
dfe2ed2a98 #EVENT - Fix for TgtUnit Name being unobtainable 2024-10-29 13:57:21 +01:00
Applevangelist
20b0902768 xxx 2024-10-29 13:56:28 +01:00
Applevangelist
6a219ab547 Merge remote-tracking branch 'origin/develop' into branch 2024-10-29 13:20:25 +01:00
Applevangelist
fd06ce8967 Merge remote-tracking branch 'origin/master' into branch 2024-10-29 13:20:17 +01:00
Applevangelist
bb5c044a25 Merge remote-tracking branch 'origin/master' into develop 2024-10-29 13:19:40 +01:00
Applevangelist
be87103b53 #CTLD - fixed a collision when extracting on a possible distance key duplication 2024-10-29 13:18:55 +01:00
Applevangelist
9d46968103 xxx 2024-10-29 13:18:00 +01:00
Applevangelist
a012184dfe Merge remote-tracking branch 'origin/develop' into branch 2024-10-29 11:39:02 +01:00
Applevangelist
972e3550f6 Merge remote-tracking branch 'origin/master' into branch
# Conflicts:
#	Moose Development/Moose/Functional/ATC_Ground.lua
2024-10-29 11:38:53 +01:00
Applevangelist
8a83f69e9c xxx 2024-10-29 11:38:06 +01:00
Applevangelist
54f7627f9c Merge remote-tracking branch 'origin/master' into develop 2024-10-29 11:37:44 +01:00
Applevangelist
2fb460c4bb #ATC_Ground - stop kicking players immediately for speeding 2024-10-29 11:37:06 +01:00
Applevangelist
1b3c94cc57 Merge remote-tracking branch 'origin/master' into develop 2024-10-27 13:25:41 +01:00
Applevangelist
216ea230a8 #BASE - Roll back to "old" serialize method for Trace 2024-10-27 13:25:13 +01:00
Applevangelist
98a82c5959 xx 2024-10-27 13:24:45 +01:00
Applevangelist
ebc7902775 Merge remote-tracking branch 'origin/develop' into branch 2024-10-26 18:46:39 +02:00
Applevangelist
7e727620a4 Merge remote-tracking branch 'origin/develop' into develop 2024-10-26 18:45:45 +02:00
Applevangelist
13ec9bcdd4 Merge remote-tracking branch 'origin/master' into develop 2024-10-26 18:45:40 +02:00
Applevangelist
90096163ee EPLRS - give group ID only if ground 2024-10-26 18:45:01 +02:00
Thomas
d2564d4a54 Merge pull request #2183 from FlightControl-Master/master
Merge from master
2024-10-21 11:35:35 +02:00
Thomas
cd178d6a8c Merge pull request #2182 from FlightControl-Master/Applevangelist-patch-1
Update MarkerOps_Base.lua
2024-10-21 11:33:14 +02:00
Thomas
2aa0b5ddfb Update MarkerOps_Base.lua
Added data to FSM events
2024-10-21 11:32:47 +02:00
Applevangelist
0473276c88 Merge remote-tracking branch 'origin/develop' into branch 2024-10-11 08:07:15 +02:00
Applevangelist
0b30ec7436 Merge remote-tracking branch 'origin/master' into branch 2024-10-11 08:07:05 +02:00
Applevangelist
cf6e026392 Merge remote-tracking branch 'origin/master' into develop 2024-10-11 08:06:29 +02:00
Applevangelist
37f819458a #AIRBOSS - Change event Land to event RunwayTouch 2024-10-11 08:06:05 +02:00
Applevangelist
84e7efc939 Merge remote-tracking branch 'origin/develop' into branch 2024-10-08 10:06:27 +02:00
Applevangelist
8bdf57d7cb Merge remote-tracking branch 'origin/master' into branch 2024-10-08 10:06:23 +02:00
Applevangelist
16fb0c3081 Merge remote-tracking branch 'origin/master' into develop 2024-10-08 10:05:56 +02:00
Applevangelist
168f4301d2 #GROUP - extended options to create callsigns for TTS with own function 2024-10-08 10:05:31 +02:00
Applevangelist
9fb03d9d8a xx 2024-10-08 10:04:05 +02:00
Applevangelist
d613f59ca8 #AWACS Added an option to set own function to create player callsigns from group or playernames 2024-10-08 10:03:45 +02:00
Applevangelist
508decd91b Merge remote-tracking branch 'origin/develop' into branch 2024-10-03 14:08:32 +02:00
Applevangelist
ff366fde74 Merge remote-tracking branch 'origin/master' into branch
# Conflicts:
#	Moose Development/Moose/Sound/SRS.lua
2024-10-03 14:08:26 +02:00
Applevangelist
d89ed535b7 Merge remote-tracking branch 'origin/master' into develop 2024-10-03 14:06:42 +02:00
Applevangelist
08b4b890ce #AWACS - added option to increase mission range from 100nm as default 2024-10-03 14:06:26 +02:00
Applevangelist
d5a406c60f #MSRS experimental - option to use PowerShell to execute SRS-TTS - set MSRS.UsePowerShell = true to test it. Known issue - PS window does not close until everything is executed. 2024-10-03 14:05:51 +02:00
Applevangelist
cb16210577 #SEAD - better event management of weapons, return earlier if no (H)ARM, return if SEAD Plane is nil 2024-10-03 14:03:06 +02:00
Applevangelist
2dce709db1 Some fixes 2024-10-03 14:02:14 +02:00
Applevangelist
cc99a5ba2a Merge remote-tracking branch 'origin/master' into branch 2024-10-01 12:10:47 +02:00
Applevangelist
8c0e0de45f xx 2024-10-01 12:10:21 +02:00
Applevangelist
e0bf137d70 Merge remote-tracking branch 'origin/develop' into branch 2024-10-01 12:09:46 +02:00
Applevangelist
ef4f1a87da Merge remote-tracking branch 'origin/master' into branch 2024-10-01 12:09:30 +02:00
Applevangelist
3524cba4ef xx 2024-10-01 12:08:51 +02:00
Applevangelist
df6a37a972 Merge remote-tracking branch 'origin/develop' into develop 2024-10-01 12:06:24 +02:00
Applevangelist
13600ff6fd Merge remote-tracking branch 'origin/master' into develop 2024-10-01 12:06:17 +02:00
Applevangelist
8ede1fb351 #DCS 2.9.8.1107 airbase updates 2024-10-01 12:03:00 +02:00
Frank
a957bb4968 Merge branch 'master' into develop 2024-10-01 09:08:03 +02:00
Frank
5f8d1cf5b0 Update Warehouse.lua
- Added check for incorrect plane attribute
2024-10-01 09:07:51 +02:00
Thomas
a115267e08 Merge pull request #2181 from DarthZyll/patch-6
Update CSAR.lua - Updated correct type name for Kiowa to OH58D
2024-10-01 06:19:29 +02:00
Applevangelist
60ce3685c9 Merge remote-tracking branch 'origin/develop' into branch 2024-09-30 11:37:17 +02:00
Applevangelist
a1d06a6fc8 Merge remote-tracking branch 'origin/master' into branch
# Conflicts:
#	Moose Development/Moose/Core/Spawn.lua
2024-09-30 11:37:07 +02:00
Applevangelist
3d5dfc422d xx 2024-09-30 11:36:19 +02:00
Applevangelist
37998fbf1d Merge remote-tracking branch 'origin/master' into develop 2024-09-30 11:36:06 +02:00
Applevangelist
846aa823d4 fixes for #SEAD and #SPAWN 2024-09-30 11:35:25 +02:00
Applevangelist
52983bc5f1 Merge remote-tracking branch 'origin/develop' into branch 2024-09-27 11:58:56 +02:00
Applevangelist
a8a1a0e37c Merge remote-tracking branch 'origin/master' into branch 2024-09-27 11:58:45 +02:00
Applevangelist
0d877853eb Merge remote-tracking branch 'origin/master' into develop 2024-09-27 11:58:08 +02:00
Applevangelist
68298fc585 #EVENT Fix for scenery sometimes nit having a getName() function 2024-09-27 11:57:40 +02:00
Applevangelist
e5965f8aec xx 2024-09-27 11:57:17 +02:00
Applevangelist
0c8a4c4cf8 xx 2024-09-27 09:04:36 +02:00
Applevangelist
f1b4bb734d Merge remote-tracking branch 'origin/develop' into branch 2024-09-26 09:08:00 +02:00
Applevangelist
5c3a5ddee3 Merge remote-tracking branch 'origin/master' into branch 2024-09-26 09:07:56 +02:00
Applevangelist
b97a7cf387 Merge remote-tracking branch 'origin/master' into develop 2024-09-26 09:07:20 +02:00
Applevangelist
e9d75f6d94 #CSAR Beacon Name Fix 2024-09-26 09:06:57 +02:00
Applevangelist
1dce2eb747 Merge remote-tracking branch 'origin/develop' into develop 2024-09-26 09:03:05 +02:00
Applevangelist
7c110d90cd Merge remote-tracking branch 'origin/master' into develop 2024-09-26 09:03:00 +02:00
Mike Young
2ecc02ce4d Update CSAR.lua - Updated correct type name for Kiowa to OH58D
Update CSAR.lua - Updated correct type name for Kiowa to OH58D - original Brykeller change
2024-09-26 01:05:38 -04:00
Thomas
8fa5277417 Merge pull request #2178 from brykeller/patch-2
Kiowa rescues sit on the skids, no doors need to be opened
2024-09-26 06:55:02 +02:00
Thomas
7cce5745af Merge pull request #2177 from DarthZyll/patch-5
Update CSAR.lua for BeaconName being built incorrectly
2024-09-26 06:54:20 +02:00
Thomas
231f1f236d Merge pull request #2180 from brykeller/patch-3
Update CTLD.lua to update the correct Kiowa typename to OH58D
2024-09-26 06:53:21 +02:00
Bryan Keller
ece0a46f97 Update CTLD.lua to update the correct Kiowa typename to OH58D
Update CTLD.lua to update the correct Kiowa typename to OH58D
2024-09-25 18:59:26 -07:00
Bryan Keller
2c9b0b8376 Kiowa rescues sit on the skids, no doors need to be opened
Kiowa rescues sit on the skids, no doors need to be opened
2024-09-25 18:51:31 -07:00
Mike Young
76577c03e7 Update CSAR.lua for BeaconName being built incorrectly
Prior to this change, the F10 menu "List Active CSAR" is always blank even after a CSAR activity occurs
2024-09-25 21:13:22 -04:00
Applevangelist
e0e4ba7db3 xx 2024-09-23 13:43:05 +02:00
Applevangelist
48d30250a0 Merge remote-tracking branch 'origin/master' into develop 2024-09-23 12:44:14 +02:00
Applevangelist
1f3df107bf xxx 2024-09-23 12:43:51 +02:00
Applevangelist
a798f2d61c Small fixes 2024-09-23 12:43:39 +02:00
Applevangelist
d0f8b1436b #EASYGCICAP - Alert5 typo correction, typo corrected to assign correct coalition side 2024-09-23 10:21:05 +02:00
Applevangelist
6887289466 xx 2024-09-23 10:19:58 +02:00
Applevangelist
9ef7a8aa3c Merge remote-tracking branch 'origin/develop' into branch 2024-09-15 19:25:46 +02:00
Applevangelist
900b0049bc Merge remote-tracking branch 'origin/master' into branch 2024-09-15 19:25:42 +02:00
Applevangelist
ddc5e0f86e Merge remote-tracking branch 'origin/master' into develop 2024-09-15 19:25:08 +02:00
Applevangelist
cadf46ded2 xx 2024-09-15 19:24:34 +02:00
Applevangelist
ae08c87822 #CTLD 2024-09-15 19:24:24 +02:00
Thomas
ae880e9d1c Merge pull request #2176 from Maintenance-Partnership-Systems/Minor-Doco-Fixes
Minor doco fixes
2024-09-15 11:16:08 +02:00
Michael Barnes
101d2e1de5 More incorrect AIRBASE constants in examples. 2024-09-15 18:48:42 +10:00
Michael Barnes
f6b7708567 Fixed some outdated references to AIRBASE constants in examples. 2024-09-15 18:34:18 +10:00
Applevangelist
b1fd5970d0 Merge remote-tracking branch 'origin/develop' into branch 2024-09-08 13:25:18 +02:00
Applevangelist
a4a4b2e6b1 Merge remote-tracking branch 'origin/master' into branch 2024-09-08 13:25:14 +02:00
Applevangelist
051286acd1 #SPAWN - Tuning measures from DEVELOP encorporated 2024-09-08 13:24:09 +02:00
Applevangelist
d58edf7c38 xx 2024-09-08 13:18:32 +02:00
Applevangelist
1f0781f19a Merge remote-tracking branch 'origin/develop' into branch 2024-09-08 11:45:35 +02:00
Applevangelist
f53488e8b0 Merge remote-tracking branch 'origin/master' into branch 2024-09-08 11:45:22 +02:00
Applevangelist
232547a0fb xxx 2024-09-08 11:44:59 +02:00
Applevangelist
f3be326b5e Merge remote-tracking branch 'origin/develop' into branch 2024-09-06 16:42:02 +02:00
Applevangelist
9703d10f03 Merge remote-tracking branch 'origin/master' into branch 2024-09-06 16:41:55 +02:00
Applevangelist
d6ecde96f8 #CTLD #CSAR Function to add own SET_GROUP for pilots 2024-09-06 16:40:18 +02:00
Applevangelist
8153052eed Merge remote-tracking branch 'origin/develop' into branch
# Conflicts:
#	Moose Development/Moose/Wrapper/Group.lua
2024-09-06 13:43:19 +02:00
Applevangelist
7d3a0568a9 Qol 2024-09-06 13:40:41 +02:00
Applevangelist
4bafdf9408 #SPAWN - relax checks on SpawnScheduled, ensure count is only done on event 2024-09-03 12:36:50 +02:00
Thomas
efe3571120 Update Spawn.lua
Remove T2 calls
2024-09-02 13:42:10 +02:00
Applevangelist
bd1fd6f469 Merge remote-tracking branch 'origin/develop' into branch 2024-09-01 15:38:14 +02:00
Applevangelist
69b962b934 Merge remote-tracking branch 'origin/master' into branch 2024-09-01 15:38:07 +02:00
Applevangelist
1a7117c20d #CONTROLLABLE - Added IR Marker Beacons for UNIT and GROUP objects 2024-09-01 15:36:20 +02:00
Applevangelist
c8b0e9e5d2 Merge remote-tracking branch 'origin/develop' into branch 2024-09-01 13:38:22 +02:00
Applevangelist
4b3e361a28 xx 2024-09-01 13:38:15 +02:00
Applevangelist
1965e45737 Merge remote-tracking branch 'origin/master' into branch
# Conflicts:
#	Moose Development/Moose/Ops/CTLD.lua
2024-09-01 12:42:29 +02:00
Applevangelist
a9ecef4f01 Merge remote-tracking branch 'origin/develop' into branch 2024-09-01 12:42:06 +02:00
Applevangelist
f11755aad3 xxx 2024-08-29 17:31:40 +02:00
Applevangelist
e42e712e77 Merge remote-tracking branch 'origin/develop' into branch 2024-08-29 10:26:10 +02:00
Applevangelist
84fbd7c4b0 Merge remote-tracking branch 'origin/master' into branch 2024-08-29 10:26:06 +02:00
Applevangelist
6fb5222358 CTLD Fix for saved crate reload duplicating crates on load 2024-08-29 10:21:24 +02:00
Applevangelist
120a66819c Merge remote-tracking branch 'origin/develop' into branch 2024-08-27 18:38:26 +02:00
Applevangelist
d110ef1340 Merge remote-tracking branch 'origin/master' into branch 2024-08-27 18:38:22 +02:00
Applevangelist
6706fc0af9 xx 2024-08-27 18:37:00 +02:00
Applevangelist
dc358f9cde xx 2024-08-27 13:18:26 +02:00
Applevangelist
e285b26b11 Merge remote-tracking branch 'origin/develop' into branch 2024-08-27 10:57:29 +02:00
Applevangelist
a62184319f Merge remote-tracking branch 'origin/master' into branch 2024-08-27 10:57:23 +02:00
Applevangelist
add0a0231a xx 2024-08-27 10:56:59 +02:00
Applevangelist
d00913cb68 Merge remote-tracking branch 'origin/develop' into branch 2024-08-26 18:32:12 +02:00
Applevangelist
eef24def61 Merge remote-tracking branch 'origin/master' into branch 2024-08-26 18:32:08 +02:00
Applevangelist
3813a30def xx 2024-08-26 18:30:52 +02:00
Applevangelist
13bde6f2ab xx 2024-08-26 18:21:03 +02:00
Applevangelist
c38e357652 Merge remote-tracking branch 'origin/develop' into branch 2024-08-26 15:54:02 +02:00
Applevangelist
06d9564b8a Merge remote-tracking branch 'origin/master' into branch 2024-08-26 15:53:58 +02:00
Applevangelist
0372735a02 xx 2024-08-26 15:51:22 +02:00
Applevangelist
deb913a95b xx 2024-08-24 18:10:46 +02:00
Applevangelist
f59dd3e8c7 Merge remote-tracking branch 'origin/develop' into branch 2024-08-23 16:30:22 +02:00
Applevangelist
b2ed5c56fc Merge remote-tracking branch 'origin/master' into branch 2024-08-23 16:30:15 +02:00
Applevangelist
9e29384262 Merge remote-tracking branch 'origin/master' into branch
# Conflicts:
#	Moose Development/Moose/Ops/CTLD.lua
#	Moose Development/Moose/Utilities/Enums.lua
2024-08-23 14:03:21 +02:00
Applevangelist
540412d115 xx 2024-08-23 12:47:08 +02:00
Applevangelist
694b7afc71 xx 2024-08-23 12:42:16 +02:00
Applevangelist
cb4a12c5ba xx 2024-08-23 12:41:44 +02:00
Applevangelist
733251bf06 xx 2024-08-22 18:27:15 +02:00
Applevangelist
42069c5bbb xx 2024-08-22 17:17:41 +02:00
Applevangelist
82b4276b33 xx 2024-08-20 18:03:41 +02:00
Applevangelist
8f3fc9cb2b xx 2024-08-20 17:56:09 +02:00
Applevangelist
89c6358085 xxx 2024-08-20 10:55:22 +02:00
Applevangelist
abb295ecc2 Merge remote-tracking branch 'origin/develop' into branch 2024-08-20 10:48:41 +02:00
Applevangelist
5607aa1cf2 Merge remote-tracking branch 'origin/master' into branch 2024-08-20 10:48:33 +02:00
Applevangelist
43f1c6fcb1 xx 2024-08-20 10:48:08 +02:00
Applevangelist
cebf21ec19 Merge remote-tracking branch 'origin/develop' into branch 2024-08-18 19:39:15 +02:00
Applevangelist
f4951d09a7 Merge remote-tracking branch 'origin/master' into branch 2024-08-18 19:39:08 +02:00
Applevangelist
21ec234dd3 xx 2024-08-18 19:38:43 +02:00
Applevangelist
43ffad02be xx 2024-08-18 19:33:24 +02:00
Applevangelist
4859ea2fb1 Merge remote-tracking branch 'origin/master' into branch 2024-08-18 13:59:41 +02:00
Applevangelist
86ed8fe9e5 #ATIS cover "NEWRAINPRESET4" 2024-08-18 13:06:13 +02:00
Applevangelist
d0d709a410 Merge remote-tracking branch 'origin/develop' into branch 2024-08-17 14:23:25 +02:00
Applevangelist
8ecb4372b5 Merge remote-tracking branch 'origin/master' into branch 2024-08-17 14:23:16 +02:00
Applevangelist
cf322c121e xx 2024-08-17 11:37:26 +02:00
Applevangelist
1aca716431 Merge remote-tracking branch 'origin/develop' into branch 2024-08-16 16:24:16 +02:00
Applevangelist
83209341ee Merge remote-tracking branch 'origin/master' into branch 2024-08-16 16:24:09 +02:00
Applevangelist
a481e46017 Merge remote-tracking branch 'origin/develop' into branch 2024-08-16 15:47:12 +02:00
Applevangelist
031a0035bd Merge remote-tracking branch 'origin/master' into branch 2024-08-16 15:47:05 +02:00
Applevangelist
4ca4d2e211 xx 2024-08-16 15:45:43 +02:00
Applevangelist
866d4f93cc Merge remote-tracking branch 'origin/develop' into branch 2024-08-16 15:23:32 +02:00
Applevangelist
98eb362628 Merge remote-tracking branch 'origin/master' into branch 2024-08-16 15:23:18 +02:00
Applevangelist
b95f12449e xx 2024-08-16 15:21:21 +02:00
Applevangelist
e7ab07cec3 Merge remote-tracking branch 'origin/develop' into branch 2024-08-16 09:45:20 +02:00
Applevangelist
c61dd606a1 Merge remote-tracking branch 'origin/master' into branch 2024-08-16 09:45:12 +02:00
Applevangelist
3d029d8f6f Merge remote-tracking branch 'origin/develop' into branch
# Conflicts:
#	Moose Development/Moose/Ops/CTLD.lua
#	Moose Development/Moose/Wrapper/DynamicCargo.lua
2024-08-16 09:41:43 +02:00
Applevangelist
9bb18a9df2 Merge remote-tracking branch 'origin/master' into branch
# Conflicts:
#	Moose Development/Moose/Ops/CTLD.lua
#	Moose Setup/Moose.files
2024-08-16 09:41:25 +02:00
Applevangelist
823930de40 xx 2024-08-16 09:27:16 +02:00
Applevangelist
c7201580d6 xx 2024-08-15 17:43:30 +02:00
Applevangelist
e61856197a #DynamicCargo - fix for FARP/Ship heights 2024-08-15 12:01:32 +02:00
Applevangelist
37d321bb5c Merge remote-tracking branch 'origin/develop' into branch 2024-08-14 15:43:57 +02:00
Applevangelist
38e16dd07e Merge remote-tracking branch 'origin/master' into branch 2024-08-14 15:43:51 +02:00
Applevangelist
64bae07877 #ATIS French Translations 2024-08-14 15:42:26 +02:00
Applevangelist
616a80cf5c Merge remote-tracking branch 'origin/develop' into branch 2024-08-14 13:48:33 +02:00
Applevangelist
18fd22be53 Merge remote-tracking branch 'origin/master' into branch 2024-08-14 13:48:26 +02:00
Applevangelist
c82359f87b #range 2024-08-14 11:34:57 +02:00
Applevangelist
d354f02507 Merge remote-tracking branch 'origin/Apple/Develop' into branch 2024-08-14 09:48:21 +02:00
Applevangelist
f612ca2fc4 Merge remote-tracking branch 'origin/develop' into branch 2024-08-14 09:48:19 +02:00
Applevangelist
9d0156908c Merge remote-tracking branch 'origin/master' into branch 2024-08-14 09:48:10 +02:00
Thomas
0ee0eb3162 Update Moose.files 2024-08-13 19:13:23 +02:00
Thomas
81653cf289 Update Modules.lua 2024-08-13 19:12:38 +02:00
Applevangelist
60d68161c4 #CTLD #DYNAMICCARGO 2024-08-13 19:07:57 +02:00
Applevangelist
ba5743c057 Merge remote-tracking branch 'origin/develop' into branch 2024-08-13 10:31:57 +02:00
Applevangelist
0ee30532ee #ATIS - Polar circle fixes 2024-08-13 10:30:36 +02:00
Applevangelist
093a60050b xx 2024-08-13 09:49:48 +02:00
Applevangelist
a1605f9a2d Merge remote-tracking branch 'origin/develop' into branch 2024-08-12 19:06:54 +02:00
Applevangelist
6bd5148de8 Merge remote-tracking branch 'origin/master' into branch 2024-08-12 19:06:45 +02:00
Applevangelist
402e72f728 CTLD small fix 2024-08-12 16:58:42 +02:00
Applevangelist
459ee72426 Merge remote-tracking branch 'origin/Apple/Develop' into branch 2024-08-12 16:18:20 +02:00
Applevangelist
174054f538 Merge remote-tracking branch 'origin/develop' into branch 2024-08-12 16:18:15 +02:00
Applevangelist
5aba0f025a Merge remote-tracking branch 'origin/master' into branch 2024-08-12 16:17:59 +02:00
Thomas
5b32d39309 Align w master changes (#2163)
* Update Base.lua (#2159)

Performance tuning - the BASE:I, F, T calls rank very high in overall number of calls taken from Moose. Ensure only the minimum number of actions based on trace state and level is taken

* Update Spawn.lua (#2161)

Updating this class as it calls BASE I,F,T a lot. Making it less noisy for some performance tuning
2024-08-12 10:28:00 +02:00
Applevangelist
8d1adca034 Merge remote-tracking branch 'origin/develop' into branch 2024-08-10 18:54:46 +02:00
Applevangelist
9d55cb6f94 Merge remote-tracking branch 'origin/master' into branch 2024-08-10 18:54:40 +02:00
Applevangelist
501fafcea6 xx 2024-08-10 18:16:26 +02:00
Applevangelist
7b4f7e9295 Merge remote-tracking branch 'origin/develop' into branch
# Conflicts:
#	Moose Development/Moose/Modules.lua
2024-08-10 18:04:02 +02:00
Applevangelist
90fa2abbe8 Merge remote-tracking branch 'origin/develop' into branch 2024-08-10 10:17:10 +02:00
Applevangelist
0532a182bb Merge remote-tracking branch 'origin/master' into branch
# Conflicts:
#	Moose Development/Moose/Ops/CTLD.lua
2024-08-10 10:17:03 +02:00
Applevangelist
29bb204e6d xx 2024-08-10 09:52:43 +02:00
Applevangelist
bf7c49708f xx 2024-08-09 10:41:21 +02:00
Applevangelist
9227252976 #Chinhook 2024-08-08 19:53:59 +02:00
Applevangelist
c138f76c8a Merge remote-tracking branch 'origin/develop' into branch 2024-08-08 13:04:49 +02:00
Applevangelist
5f4be987c6 Merge remote-tracking branch 'origin/master' into branch 2024-08-08 13:04:46 +02:00
Applevangelist
63dc9b2e8e #CTLD - Dyn CArgo Spawns Handling 2024-08-06 18:19:35 +02:00
Applevangelist
f53bd8f11a #CTLD
* Added spawning crates behind the CH-47
* Added functionality to retain resource maps of cargo statics
* Added door check for cargo crate operations
2024-08-04 17:35:06 +02:00
Applevangelist
554809764f #AIRBASE - Added Kola airbases 2024-08-04 17:33:53 +02:00
Applevangelist
084c172e93 #SPAWN - Added StopRepeat() 2024-08-04 17:33:39 +02:00
Applevangelist
d86f471361 #SPAWNSTATIC - added functions to manage cargo static resource maps 2024-08-04 17:33:22 +02:00
Applevangelist
7fa01b9e3c #STATIC - added access to STORAGE object if set (cargo static types) 2024-08-04 17:32:46 +02:00
Applevangelist
5ffeecc333 #STORAGE - added enumerators 2024-08-04 17:32:21 +02:00
Applevangelist
0d88fec4bf #UTILS - added rear door check for CH-47 2024-08-04 17:32:07 +02:00
Applevangelist
06d4e4ac1c Merge remote-tracking branch 'origin/develop' into branch 2024-08-03 14:34:20 +02:00
Applevangelist
b3758ae0e7 Merge remote-tracking branch 'origin/master' into branch 2024-08-03 14:34:18 +02:00
Applevangelist
6a0b7896a4 Merge remote-tracking branch 'origin/Apple/Develop' into branch
# Conflicts:
#	Moose Development/Moose/Modules.lua
#	Moose Setup/Moose.files
2024-08-03 14:10:54 +02:00
Applevangelist
cc0bbf3a17 Merge remote-tracking branch 'origin/develop' into branch
# Conflicts:
#	Moose Development/Moose/Modules.lua
#	Moose Development/Moose/Modules_local.lua
#	Moose Setup/Moose.files
2024-08-03 14:09:04 +02:00
138 changed files with 9175 additions and 48952 deletions

View File

@@ -1953,7 +1953,7 @@ local function refct_from_id(id) -- refct = refct_from_id(CTypeID)
unsigned = refct.unsigned,
size = bit.band(bit.rshift(ctype.info, 16), 127),
}
refct.bool, refct.const, refct.volatile, refct.unsigned = nil
refct.bool, refct.const, refct.volatile, refct.unsigned = nil, nil, nil, nil
end
if CT[4] then -- Merge sibling attributes onto this type.

2
Moose Development/Moose/AI/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
/AI_Patrol.lua
/AI_BAI.lua

View File

@@ -1,218 +0,0 @@
--- **AI** - Models the process of Combat Air Patrol (CAP) for airplanes.
--
-- This is a class used in the @{AI.AI_A2A_Dispatcher}.
--
-- ===
--
-- ### Author: **FlightControl**
--
-- ===
--
-- @module AI.AI_A2A_Cap
-- @image AI_Combat_Air_Patrol.JPG
-- @type AI_A2A_CAP
-- @extends AI.AI_Air_Patrol#AI_AIR_PATROL
-- @extends AI.AI_Air_Engage#AI_AIR_ENGAGE
--- The AI_A2A_CAP class implements the core functions to patrol a @{Core.Zone} by an AI @{Wrapper.Group} or @{Wrapper.Group}
-- and automatically engage any airborne enemies that are within a certain range or within a certain zone.
--
-- ![Process](..\Presentations\AI_CAP\Dia3.JPG)
--
-- The AI_A2A_CAP is assigned a @{Wrapper.Group} and this must be done before the AI_A2A_CAP process can be started using the **Start** event.
--
-- ![Process](..\Presentations\AI_CAP\Dia4.JPG)
--
-- The AI will fly towards the random 3D point within the patrol zone, using a random speed within the given altitude and speed limits.
-- Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits.
--
-- ![Process](..\Presentations\AI_CAP\Dia5.JPG)
--
-- This cycle will continue.
--
-- ![Process](..\Presentations\AI_CAP\Dia6.JPG)
--
-- During the patrol, the AI will detect enemy targets, which are reported through the **Detected** event.
--
-- ![Process](..\Presentations\AI_CAP\Dia9.JPG)
--
-- When enemies are detected, the AI will automatically engage the enemy.
--
-- ![Process](..\Presentations\AI_CAP\Dia10.JPG)
--
-- Until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
-- When the fuel threshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
--
-- ![Process](..\Presentations\AI_CAP\Dia13.JPG)
--
-- ## 1. AI_A2A_CAP constructor
--
-- * @{#AI_A2A_CAP.New}(): Creates a new AI_A2A_CAP object.
--
-- ## 2. AI_A2A_CAP is a FSM
--
-- ![Process](..\Presentations\AI_CAP\Dia2.JPG)
--
-- ### 2.1 AI_A2A_CAP States
--
-- * **None** ( Group ): The process is not started yet.
-- * **Patrolling** ( Group ): The AI is patrolling the Patrol Zone.
-- * **Engaging** ( Group ): The AI is engaging the bogeys.
-- * **Returning** ( Group ): The AI is returning to Base..
--
-- ### 2.2 AI_A2A_CAP Events
--
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Start}**: Start the process.
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Route}**: Route the AI to a new random 3D point within the Patrol Zone.
-- * **@{#AI_A2A_CAP.Engage}**: Let the AI engage the bogeys.
-- * **@{#AI_A2A_CAP.Abort}**: Aborts the engagement and return patrolling in the patrol zone.
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.RTB}**: Route the AI to the home base.
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detect}**: The AI is detecting targets.
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets.
-- * **@{#AI_A2A_CAP.Destroy}**: The AI has destroyed a bogey @{Wrapper.Unit}.
-- * **@{#AI_A2A_CAP.Destroyed}**: The AI has destroyed all bogeys @{Wrapper.Unit}s assigned in the CAS task.
-- * **Status** ( Group ): The AI is checking status (fuel and damage). When the thresholds have been reached, the AI will RTB.
--
-- ## 3. Set the Range of Engagement
--
-- ![Range](..\Presentations\AI_CAP\Dia11.JPG)
--
-- An optional range can be set in meters,
-- that will define when the AI will engage with the detected airborne enemy targets.
-- The range can be beyond or smaller than the range of the Patrol Zone.
-- The range is applied at the position of the AI.
-- Use the method @{#AI_A2A_CAP.SetEngageRange}() to define that range.
--
-- ## 4. Set the Zone of Engagement
--
-- ![Zone](..\Presentations\AI_CAP\Dia12.JPG)
--
-- An optional @{Core.Zone} can be set,
-- that will define when the AI will engage with the detected airborne enemy targets.
-- Use the method @{#AI_A2A_CAP.SetEngageZone}() to define that Zone.
--
-- # Developer Note
--
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
-- Therefore, this class is considered to be deprecated
--
-- ===
--
-- @field #AI_A2A_CAP
AI_A2A_CAP = {
ClassName = "AI_A2A_CAP",
}
--- Creates a new AI_A2A_CAP object
-- @param #AI_A2A_CAP self
-- @param Wrapper.Group#GROUP AICap
-- @param DCS#Speed EngageMinSpeed The minimum speed of the @{Wrapper.Group} in km/h when engaging a target.
-- @param DCS#Speed EngageMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h when engaging a target.
-- @param DCS#Altitude EngageFloorAltitude The lowest altitude in meters where to execute the engagement.
-- @param DCS#Altitude EngageCeilingAltitude The highest altitude in meters where to execute the engagement.
-- @param DCS#AltitudeType EngageAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to "RADIO".
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Core.Zone} where the patrol needs to be executed.
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h.
-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h.
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
-- @param DCS#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to "RADIO".
-- @return #AI_A2A_CAP
function AI_A2A_CAP:New2( AICap, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType, PatrolZone, PatrolMinSpeed, PatrolMaxSpeed, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolAltType )
-- Multiple inheritance ... :-)
local AI_Air = AI_AIR:New( AICap )
local AI_Air_Patrol = AI_AIR_PATROL:New( AI_Air, AICap, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType )
local AI_Air_Engage = AI_AIR_ENGAGE:New( AI_Air_Patrol, AICap, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType )
local self = BASE:Inherit( self, AI_Air_Engage ) --#AI_A2A_CAP
self:SetFuelThreshold( .2, 60 )
self:SetDamageThreshold( 0.4 )
self:SetDisengageRadius( 70000 )
return self
end
--- Creates a new AI_A2A_CAP object
-- @param #AI_A2A_CAP self
-- @param Wrapper.Group#GROUP AICap
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Core.Zone} where the patrol needs to be executed.
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h.
-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h.
-- @param DCS#Speed EngageMinSpeed The minimum speed of the @{Wrapper.Group} in km/h when engaging a target.
-- @param DCS#Speed EngageMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h when engaging a target.
-- @param DCS#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO
-- @return #AI_A2A_CAP
function AI_A2A_CAP:New( AICap, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, EngageMinSpeed, EngageMaxSpeed, PatrolAltType )
return self:New2( AICap, EngageMinSpeed, EngageMaxSpeed, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolAltType, PatrolZone, PatrolMinSpeed, PatrolMaxSpeed, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolAltType )
end
--- onafter State Transition for Event Patrol.
-- @param #AI_A2A_CAP self
-- @param Wrapper.Group#GROUP AICap The AI Group managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_A2A_CAP:onafterStart( AICap, From, Event, To )
self:GetParent( self, AI_A2A_CAP ).onafterStart( self, AICap, From, Event, To )
AICap:HandleEvent( EVENTS.Takeoff, nil, self )
end
--- Set the Engage Zone which defines where the AI will engage bogies.
-- @param #AI_A2A_CAP self
-- @param Core.Zone#ZONE EngageZone The zone where the AI is performing CAP.
-- @return #AI_A2A_CAP self
function AI_A2A_CAP:SetEngageZone( EngageZone )
self:F2()
if EngageZone then
self.EngageZone = EngageZone
else
self.EngageZone = nil
end
end
--- Set the Engage Range when the AI will engage with airborne enemies.
-- @param #AI_A2A_CAP self
-- @param #number EngageRange The Engage Range.
-- @return #AI_A2A_CAP self
function AI_A2A_CAP:SetEngageRange( EngageRange )
self:F2()
if EngageRange then
self.EngageRange = EngageRange
else
self.EngageRange = nil
end
end
--- Evaluate the attack and create an AttackUnitTask list.
-- @param #AI_A2A_CAP self
-- @param Core.Set#SET_UNIT AttackSetUnit The set of units to attack.
-- @param Wrapper.Group#GROUP DefenderGroup The group of defenders.
-- @param #number EngageAltitude The altitude to engage the targets.
-- @return #AI_A2A_CAP self
function AI_A2A_CAP:CreateAttackUnitTasks( AttackSetUnit, DefenderGroup, EngageAltitude )
local AttackUnitTasks = {}
for AttackUnitID, AttackUnit in pairs( self.AttackSetUnit:GetSet() ) do
local AttackUnit = AttackUnit -- Wrapper.Unit#UNIT
if AttackUnit and AttackUnit:IsAlive() and AttackUnit:IsAir() then
-- TODO: Add coalition check? Only attack units of if AttackUnit:GetCoalition()~=AICap:GetCoalition()
-- Maybe the detected set also contains
self:T( { "Attacking Task:", AttackUnit:GetName(), AttackUnit:IsAlive(), AttackUnit:IsAir() } )
AttackUnitTasks[#AttackUnitTasks+1] = DefenderGroup:TaskAttackUnit( AttackUnit )
end
end
return AttackUnitTasks
end

File diff suppressed because it is too large Load Diff

View File

@@ -1,147 +0,0 @@
--- **AI** - Models the process of Ground Controlled Interception (GCI) for airplanes.
--
-- This is a class used in the @{AI.AI_A2A_Dispatcher}.
--
-- ===
--
-- ### Author: **FlightControl**
--
-- ===
--
-- @module AI.AI_A2A_Gci
-- @image AI_Ground_Control_Intercept.JPG
-- @type AI_A2A_GCI
-- @extends AI.AI_A2A#AI_A2A
--- Implements the core functions to intercept intruders. Use the Engage trigger to intercept intruders.
--
-- The AI_A2A_GCI is assigned a @{Wrapper.Group} and this must be done before the AI_A2A_GCI process can be started using the **Start** event.
--
-- The AI will fly towards the random 3D point within the patrol zone, using a random speed within the given altitude and speed limits.
-- Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits.
--
-- This cycle will continue.
--
-- During the patrol, the AI will detect enemy targets, which are reported through the **Detected** event.
--
-- When enemies are detected, the AI will automatically engage the enemy.
--
-- Until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
-- When the fuel threshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
--
-- ## 1. AI_A2A_GCI constructor
--
-- * @{#AI_A2A_GCI.New}(): Creates a new AI_A2A_GCI object.
--
-- ## 2. AI_A2A_GCI is a FSM
--
-- ![Process](..\Presentations\AI_GCI\Dia2.JPG)
--
-- ### 2.1 AI_A2A_GCI States
--
-- * **None** ( Group ): The process is not started yet.
-- * **Patrolling** ( Group ): The AI is patrolling the Patrol Zone.
-- * **Engaging** ( Group ): The AI is engaging the bogeys.
-- * **Returning** ( Group ): The AI is returning to Base..
--
-- ### 2.2 AI_A2A_GCI Events
--
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Start}**: Start the process.
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Route}**: Route the AI to a new random 3D point within the Patrol Zone.
-- * **@{#AI_A2A_GCI.Engage}**: Let the AI engage the bogeys.
-- * **@{#AI_A2A_GCI.Abort}**: Aborts the engagement and return patrolling in the patrol zone.
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.RTB}**: Route the AI to the home base.
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detect}**: The AI is detecting targets.
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets.
-- * **@{#AI_A2A_GCI.Destroy}**: The AI has destroyed a bogey @{Wrapper.Unit}.
-- * **@{#AI_A2A_GCI.Destroyed}**: The AI has destroyed all bogeys @{Wrapper.Unit}s assigned in the CAS task.
-- * **Status** ( Group ): The AI is checking status (fuel and damage). When the thresholds have been reached, the AI will RTB.
--
-- # Developer Note
--
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
-- Therefore, this class is considered to be deprecated
--
-- ===
--
-- @field #AI_A2A_GCI
AI_A2A_GCI = {
ClassName = "AI_A2A_GCI",
}
--- Creates a new AI_A2A_GCI object
-- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIIntercept
-- @param DCS#Speed EngageMinSpeed The minimum speed of the @{Wrapper.Group} in km/h when engaging a target.
-- @param DCS#Speed EngageMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h when engaging a target.
-- @param DCS#Altitude EngageFloorAltitude The lowest altitude in meters where to execute the engagement.
-- @param DCS#Altitude EngageCeilingAltitude The highest altitude in meters where to execute the engagement.
-- @param DCS#AltitudeType EngageAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to "RADIO".
-- @return #AI_A2A_GCI
function AI_A2A_GCI:New2( AIIntercept, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType )
local AI_Air = AI_AIR:New( AIIntercept )
local AI_Air_Engage = AI_AIR_ENGAGE:New( AI_Air, AIIntercept, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType )
local self = BASE:Inherit( self, AI_Air_Engage ) -- #AI_A2A_GCI
self:SetFuelThreshold( .2, 60 )
self:SetDamageThreshold( 0.4 )
self:SetDisengageRadius( 70000 )
return self
end
--- Creates a new AI_A2A_GCI object
-- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIIntercept
-- @param DCS#Speed EngageMinSpeed The minimum speed of the @{Wrapper.Group} in km/h when engaging a target.
-- @param DCS#Speed EngageMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h when engaging a target.
-- @param DCS#Altitude EngageFloorAltitude The lowest altitude in meters where to execute the engagement.
-- @param DCS#Altitude EngageCeilingAltitude The highest altitude in meters where to execute the engagement.
-- @param DCS#AltitudeType EngageAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to "RADIO".
-- @return #AI_A2A_GCI
function AI_A2A_GCI:New( AIIntercept, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType )
return self:New2( AIIntercept, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType )
end
--- onafter State Transition for Event Patrol.
-- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIIntercept The AI Group managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_A2A_GCI:onafterStart( AIIntercept, From, Event, To )
self:GetParent( self, AI_A2A_GCI ).onafterStart( self, AIIntercept, From, Event, To )
end
--- Evaluate the attack and create an AttackUnitTask list.
-- @param #AI_A2A_GCI self
-- @param Core.Set#SET_UNIT AttackSetUnit The set of units to attack.
-- @param Wrapper.Group#GROUP DefenderGroup The group of defenders.
-- @param #number EngageAltitude The altitude to engage the targets.
-- @return #AI_A2A_GCI self
function AI_A2A_GCI:CreateAttackUnitTasks( AttackSetUnit, DefenderGroup, EngageAltitude )
local AttackUnitTasks = {}
for AttackUnitID, AttackUnit in pairs( self.AttackSetUnit:GetSet() ) do
local AttackUnit = AttackUnit -- Wrapper.Unit#UNIT
self:T( { "Attacking Unit:", AttackUnit:GetName(), AttackUnit:IsAlive(), AttackUnit:IsAir() } )
if AttackUnit:IsAlive() and AttackUnit:IsAir() then
-- TODO: Add coalition check? Only attack units of if AttackUnit:GetCoalition()~=AICap:GetCoalition()
-- Maybe the detected set also contains
AttackUnitTasks[#AttackUnitTasks+1] = DefenderGroup:TaskAttackUnit( AttackUnit )
end
end
return AttackUnitTasks
end

View File

@@ -1,409 +0,0 @@
--- **AI** - Models the process of air patrol of airplanes.
--
-- ===
--
-- ### Author: **FlightControl**
--
-- ===
--
-- @module AI.AI_A2A_Patrol
-- @image AI_Air_Patrolling.JPG
-- @type AI_A2A_PATROL
-- @extends AI.AI_A2A#AI_A2A
--- Implements the core functions to patrol a @{Core.Zone} by an AI @{Wrapper.Group} or @{Wrapper.Group}.
--
-- ![Process](..\Presentations\AI_PATROL\Dia3.JPG)
--
-- The AI_A2A_PATROL is assigned a @{Wrapper.Group} and this must be done before the AI_A2A_PATROL process can be started using the **Start** event.
--
-- ![Process](..\Presentations\AI_PATROL\Dia4.JPG)
--
-- The AI will fly towards the random 3D point within the patrol zone, using a random speed within the given altitude and speed limits.
-- Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits.
--
-- ![Process](..\Presentations\AI_PATROL\Dia5.JPG)
--
-- This cycle will continue.
--
-- ![Process](..\Presentations\AI_PATROL\Dia6.JPG)
--
-- During the patrol, the AI will detect enemy targets, which are reported through the **Detected** event.
--
-- ![Process](..\Presentations\AI_PATROL\Dia9.JPG)
--
---- Note that the enemy is not engaged! To model enemy engagement, either tailor the **Detected** event, or
-- use derived AI_ classes to model AI offensive or defensive behaviour.
--
-- ![Process](..\Presentations\AI_PATROL\Dia10.JPG)
--
-- Until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
-- When the fuel threshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
--
-- ![Process](..\Presentations\AI_PATROL\Dia11.JPG)
--
-- ## 1. AI_A2A_PATROL constructor
--
-- * @{#AI_A2A_PATROL.New}(): Creates a new AI_A2A_PATROL object.
--
-- ## 2. AI_A2A_PATROL is a FSM
--
-- ![Process](..\Presentations\AI_PATROL\Dia2.JPG)
--
-- ### 2.1. AI_A2A_PATROL States
--
-- * **None** ( Group ): The process is not started yet.
-- * **Patrolling** ( Group ): The AI is patrolling the Patrol Zone.
-- * **Returning** ( Group ): The AI is returning to Base.
-- * **Stopped** ( Group ): The process is stopped.
-- * **Crashed** ( Group ): The AI has crashed or is dead.
--
-- ### 2.2. AI_A2A_PATROL Events
--
-- * **Start** ( Group ): Start the process.
-- * **Stop** ( Group ): Stop the process.
-- * **Route** ( Group ): Route the AI to a new random 3D point within the Patrol Zone.
-- * **RTB** ( Group ): Route the AI to the home base.
-- * **Detect** ( Group ): The AI is detecting targets.
-- * **Detected** ( Group ): The AI has detected new targets.
-- * **Status** ( Group ): The AI is checking status (fuel and damage). When the thresholds have been reached, the AI will RTB.
--
-- ## 3. Set or Get the AI controllable
--
-- * @{#AI_A2A_PATROL.SetControllable}(): Set the AIControllable.
-- * @{#AI_A2A_PATROL.GetControllable}(): Get the AIControllable.
--
-- ## 4. Set the Speed and Altitude boundaries of the AI controllable
--
-- * @{#AI_A2A_PATROL.SetSpeed}(): Set the patrol speed boundaries of the AI, for the next patrol.
-- * @{#AI_A2A_PATROL.SetAltitude}(): Set altitude boundaries of the AI, for the next patrol.
--
-- ## 5. Manage the detection process of the AI controllable
--
-- The detection process of the AI controllable can be manipulated.
-- Detection requires an amount of CPU power, which has an impact on your mission performance.
-- Only put detection on when absolutely necessary, and the frequency of the detection can also be set.
--
-- * @{#AI_A2A_PATROL.SetDetectionOn}(): Set the detection on. The AI will detect for targets.
-- * @{#AI_A2A_PATROL.SetDetectionOff}(): Set the detection off, the AI will not detect for targets. The existing target list will NOT be erased.
--
-- The detection frequency can be set with @{#AI_A2A_PATROL.SetRefreshTimeInterval}( seconds ), where the amount of seconds specify how much seconds will be waited before the next detection.
-- Use the method @{#AI_A2A_PATROL.GetDetectedUnits}() to obtain a list of the @{Wrapper.Unit}s detected by the AI.
--
-- The detection can be filtered to potential targets in a specific zone.
-- Use the method @{#AI_A2A_PATROL.SetDetectionZone}() to set the zone where targets need to be detected.
-- Note that when the zone is too far away, or the AI is not heading towards the zone, or the AI is too high, no targets may be detected
-- according the weather conditions.
--
-- ## 6. Manage the "out of fuel" in the AI_A2A_PATROL
--
-- When the AI is out of fuel, it is required that a new AI is started, before the old AI can return to the home base.
-- Therefore, with a parameter and a calculation of the distance to the home base, the fuel threshold is calculated.
-- When the fuel threshold is reached, the AI will continue for a given time its patrol task in orbit,
-- while a new AI is targeted to the AI_A2A_PATROL.
-- Once the time is finished, the old AI will return to the base.
-- Use the method @{#AI_A2A_PATROL.ManageFuel}() to have this proces in place.
--
-- ## 7. Manage "damage" behaviour of the AI in the AI_A2A_PATROL
--
-- When the AI is damaged, it is required that a new Patrol is started. However, damage cannon be foreseen early on.
-- Therefore, when the damage threshold is reached, the AI will return immediately to the home base (RTB).
-- Use the method @{#AI_A2A_PATROL.ManageDamage}() to have this proces in place.
--
-- # Developer Note
--
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
-- Therefore, this class is considered to be deprecated
--
-- ===
--
-- @field #AI_A2A_PATROL
AI_A2A_PATROL = {
ClassName = "AI_A2A_PATROL",
}
--- Creates a new AI_A2A_PATROL object
-- @param #AI_A2A_PATROL self
-- @param Wrapper.Group#GROUP AIPatrol The patrol group object.
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Core.Zone} where the patrol needs to be executed.
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h.
-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h.
-- @param DCS#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to BARO
-- @return #AI_A2A_PATROL self
-- @usage
-- -- Define a new AI_A2A_PATROL Object. This PatrolArea will patrol a Group within PatrolZone between 3000 and 6000 meters, with a variying speed between 600 and 900 km/h.
-- PatrolZone = ZONE:New( 'PatrolZone' )
-- PatrolSpawn = SPAWN:New( 'Patrol Group' )
-- PatrolArea = AI_A2A_PATROL:New( PatrolZone, 3000, 6000, 600, 900 )
function AI_A2A_PATROL:New( AIPatrol, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType )
local AI_Air = AI_AIR:New( AIPatrol )
local AI_Air_Patrol = AI_AIR_PATROL:New( AI_Air, AIPatrol, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType )
local self = BASE:Inherit( self, AI_Air_Patrol ) -- #AI_A2A_PATROL
self:SetFuelThreshold( .2, 60 )
self:SetDamageThreshold( 0.4 )
self:SetDisengageRadius( 70000 )
self.PatrolZone = PatrolZone
self.PatrolFloorAltitude = PatrolFloorAltitude
self.PatrolCeilingAltitude = PatrolCeilingAltitude
self.PatrolMinSpeed = PatrolMinSpeed
self.PatrolMaxSpeed = PatrolMaxSpeed
-- defafult PatrolAltType to "BARO" if not specified
self.PatrolAltType = PatrolAltType or "BARO"
self:AddTransition( { "Started", "Airborne", "Refuelling" }, "Patrol", "Patrolling" )
--- OnBefore Transition Handler for Event Patrol.
-- @function [parent=#AI_A2A_PATROL] OnBeforePatrol
-- @param #AI_A2A_PATROL self
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Patrol.
-- @function [parent=#AI_A2A_PATROL] OnAfterPatrol
-- @param #AI_A2A_PATROL self
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Patrol.
-- @function [parent=#AI_A2A_PATROL] Patrol
-- @param #AI_A2A_PATROL self
--- Asynchronous Event Trigger for Event Patrol.
-- @function [parent=#AI_A2A_PATROL] __Patrol
-- @param #AI_A2A_PATROL self
-- @param #number Delay The delay in seconds.
--- OnLeave Transition Handler for State Patrolling.
-- @function [parent=#AI_A2A_PATROL] OnLeavePatrolling
-- @param #AI_A2A_PATROL self
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnEnter Transition Handler for State Patrolling.
-- @function [parent=#AI_A2A_PATROL] OnEnterPatrolling
-- @param #AI_A2A_PATROL self
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
self:AddTransition( "Patrolling", "Route", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_PATROL.
--- OnBefore Transition Handler for Event Route.
-- @function [parent=#AI_A2A_PATROL] OnBeforeRoute
-- @param #AI_A2A_PATROL self
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Route.
-- @function [parent=#AI_A2A_PATROL] OnAfterRoute
-- @param #AI_A2A_PATROL self
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Route.
-- @function [parent=#AI_A2A_PATROL] Route
-- @param #AI_A2A_PATROL self
--- Asynchronous Event Trigger for Event Route.
-- @function [parent=#AI_A2A_PATROL] __Route
-- @param #AI_A2A_PATROL self
-- @param #number Delay The delay in seconds.
self:AddTransition( "*", "Reset", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_PATROL.
return self
end
--- Sets (modifies) the minimum and maximum speed of the patrol.
-- @param #AI_A2A_PATROL self
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h.
-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h.
-- @return #AI_A2A_PATROL self
function AI_A2A_PATROL:SetSpeed( PatrolMinSpeed, PatrolMaxSpeed )
self:F2( { PatrolMinSpeed, PatrolMaxSpeed } )
self.PatrolMinSpeed = PatrolMinSpeed
self.PatrolMaxSpeed = PatrolMaxSpeed
end
--- Sets the floor and ceiling altitude of the patrol.
-- @param #AI_A2A_PATROL self
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
-- @return #AI_A2A_PATROL self
function AI_A2A_PATROL:SetAltitude( PatrolFloorAltitude, PatrolCeilingAltitude )
self:F2( { PatrolFloorAltitude, PatrolCeilingAltitude } )
self.PatrolFloorAltitude = PatrolFloorAltitude
self.PatrolCeilingAltitude = PatrolCeilingAltitude
end
--- Defines a new patrol route using the @{AI.AI_Patrol#AI_PATROL_ZONE} parameters and settings.
-- @param #AI_A2A_PATROL self
-- @return #AI_A2A_PATROL self
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_A2A_PATROL:onafterPatrol( AIPatrol, From, Event, To )
self:F2()
self:ClearTargetDistance()
self:__Route( 1 )
AIPatrol:OnReSpawn(
function( PatrolGroup )
self:__Reset( 1 )
self:__Route( 5 )
end
)
end
--- This static method is called from the route path within the last task at the last waypoint of the AIPatrol.
-- Note that this method is required, as triggers the next route when patrolling for the AIPatrol.
-- @param Wrapper.Group#GROUP AIPatrol The AI group.
-- @param #AI_A2A_PATROL Fsm The FSM.
function AI_A2A_PATROL.PatrolRoute( AIPatrol, Fsm )
AIPatrol:F( { "AI_A2A_PATROL.PatrolRoute:", AIPatrol:GetName() } )
if AIPatrol and AIPatrol:IsAlive() then
Fsm:Route()
end
end
--- Defines a new patrol route using the @{AI.AI_Patrol#AI_PATROL_ZONE} parameters and settings.
-- @param #AI_A2A_PATROL self
-- @param Wrapper.Group#GROUP AIPatrol The Group managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_A2A_PATROL:onafterRoute( AIPatrol, From, Event, To )
self:F2()
-- When RTB, don't allow anymore the routing.
if From == "RTB" then
return
end
if AIPatrol and AIPatrol:IsAlive() then
local PatrolRoute = {}
--- Calculate the target route point.
local CurrentCoord = AIPatrol:GetCoordinate()
-- Random altitude.
local altitude=math.random(self.PatrolFloorAltitude, self.PatrolCeilingAltitude)
-- Random speed in km/h.
local speedkmh = math.random(self.PatrolMinSpeed, self.PatrolMaxSpeed)
-- First waypoint is current position.
PatrolRoute[1]=CurrentCoord:WaypointAirTurningPoint(nil, speedkmh, {}, "Current")
if self.racetrack then
-- Random heading.
local heading = math.random(self.racetrackheadingmin, self.racetrackheadingmax)
-- Random leg length.
local leg=math.random(self.racetracklegmin, self.racetracklegmax)
-- Random duration if any.
local duration = self.racetrackdurationmin
if self.racetrackdurationmax then
duration=math.random(self.racetrackdurationmin, self.racetrackdurationmax)
end
-- CAP coordinate.
local c0=self.PatrolZone:GetRandomCoordinate()
if self.racetrackcapcoordinates and #self.racetrackcapcoordinates>0 then
c0=self.racetrackcapcoordinates[math.random(#self.racetrackcapcoordinates)]
end
-- Race track points.
local c1=c0:SetAltitude(altitude) --Core.Point#COORDINATE
local c2=c1:Translate(leg, heading):SetAltitude(altitude)
self:SetTargetDistance(c0) -- For RTB status check
-- Debug:
self:T(string.format("Patrol zone race track: v=%.1f knots, h=%.1f ft, heading=%03d, leg=%d m, t=%s sec", UTILS.KmphToKnots(speedkmh), UTILS.MetersToFeet(altitude), heading, leg, tostring(duration)))
--c1:MarkToAll("Race track c1")
--c2:MarkToAll("Race track c2")
-- Task to orbit.
local taskOrbit=AIPatrol:TaskOrbit(c1, altitude, UTILS.KmphToMps(speedkmh), c2)
-- Task function to redo the patrol at other random position.
local taskPatrol=AIPatrol:TaskFunction("AI_A2A_PATROL.PatrolRoute", self)
-- Controlled task with task condition.
local taskCond=AIPatrol:TaskCondition(nil, nil, nil, nil, duration, nil)
local taskCont=AIPatrol:TaskControlled(taskOrbit, taskCond)
-- Second waypoint
PatrolRoute[2]=c1:WaypointAirTurningPoint(self.PatrolAltType, speedkmh, {taskCont, taskPatrol}, "CAP Orbit")
else
-- Target coordinate.
local ToTargetCoord=self.PatrolZone:GetRandomCoordinate() --Core.Point#COORDINATE
ToTargetCoord:SetAltitude(altitude)
self:SetTargetDistance( ToTargetCoord ) -- For RTB status check
local taskReRoute=AIPatrol:TaskFunction( "AI_A2A_PATROL.PatrolRoute", self )
PatrolRoute[2]=ToTargetCoord:WaypointAirTurningPoint(self.PatrolAltType, speedkmh, {taskReRoute}, "Patrol Point")
end
-- ROE
AIPatrol:OptionROEReturnFire()
AIPatrol:OptionROTEvadeFire()
-- Patrol.
AIPatrol:Route( PatrolRoute, 0.5)
end
end

View File

@@ -1,96 +0,0 @@
--- **AI** - Models the process of air to ground BAI engagement for airplanes and helicopters.
--
-- This is a class used in the @{AI.AI_A2G_Dispatcher}.
--
-- ===
--
-- ### Author: **FlightControl**
--
-- ===
--
-- @module AI.AI_A2G_BAI
-- @image AI_Air_To_Ground_Engage.JPG
-- @type AI_A2G_BAI
-- @extends AI.AI_A2A_Engage#AI_A2A_Engage -- TODO: Documentation. This class does not exist, unable to determine what it extends.
--- Implements the core functions to intercept intruders. Use the Engage trigger to intercept intruders.
--
-- # Developer Note
--
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
-- Therefore, this class is considered to be deprecated
--
-- ===
--
-- @field #AI_A2G_BAI
AI_A2G_BAI = {
ClassName = "AI_A2G_BAI",
}
--- Creates a new AI_A2G_BAI object
-- @param #AI_A2G_BAI self
-- @param Wrapper.Group#GROUP AIGroup
-- @param DCS#Speed EngageMinSpeed The minimum speed of the @{Wrapper.Group} in km/h when engaging a target.
-- @param DCS#Speed EngageMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h when engaging a target.
-- @param DCS#Altitude EngageFloorAltitude The lowest altitude in meters where to execute the engagement.
-- @param DCS#Altitude EngageCeilingAltitude The highest altitude in meters where to execute the engagement.
-- @param DCS#AltitudeType EngageAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to "RADIO".
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Core.Zone} where the patrol needs to be executed.
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h.
-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h.
-- @param DCS#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO
-- @return #AI_A2G_BAI
function AI_A2G_BAI:New2( AIGroup, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType )
local AI_Air = AI_AIR:New( AIGroup )
local AI_Air_Patrol = AI_AIR_PATROL:New( AI_Air, AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType ) -- #AI_AIR_PATROL
local AI_Air_Engage = AI_AIR_ENGAGE:New( AI_Air_Patrol, AIGroup, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType )
local self = BASE:Inherit( self, AI_Air_Engage )
return self
end
--- Creates a new AI_A2G_BAI object
-- @param #AI_A2G_BAI self
-- @param Wrapper.Group#GROUP AIGroup
-- @param DCS#Speed EngageMinSpeed The minimum speed of the @{Wrapper.Group} in km/h when engaging a target.
-- @param DCS#Speed EngageMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h when engaging a target.
-- @param DCS#Altitude EngageFloorAltitude The lowest altitude in meters where to execute the engagement.
-- @param DCS#Altitude EngageCeilingAltitude The highest altitude in meters where to execute the engagement.
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Core.Zone} where the patrol needs to be executed.
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h.
-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h.
-- @param DCS#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO
-- @return #AI_A2G_BAI
function AI_A2G_BAI:New( AIGroup, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType )
return self:New2( AIGroup, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, PatrolAltType, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType)
end
--- Evaluate the attack and create an AttackUnitTask list.
-- @param #AI_A2G_BAI self
-- @param Core.Set#SET_UNIT AttackSetUnit The set of units to attack.
-- @param Wrapper.Group#GROUP DefenderGroup The group of defenders.
-- @param #number EngageAltitude The altitude to engage the targets.
-- @return #AI_A2G_BAI self
function AI_A2G_BAI:CreateAttackUnitTasks( AttackSetUnit, DefenderGroup, EngageAltitude )
local AttackUnitTasks = {}
local AttackSetUnitPerThreatLevel = AttackSetUnit:GetSetPerThreatLevel( 10, 0 )
for AttackUnitIndex, AttackUnit in ipairs( AttackSetUnitPerThreatLevel or {} ) do
if AttackUnit then
if AttackUnit:IsAlive() and AttackUnit:IsGround() then
self:T( { "BAI Unit:", AttackUnit:GetName() } )
AttackUnitTasks[#AttackUnitTasks+1] = DefenderGroup:TaskAttackUnit( AttackUnit, true, false, nil, nil, EngageAltitude )
end
end
end
return AttackUnitTasks
end

View File

@@ -1,96 +0,0 @@
--- **AI** - Models the process of air to ground engagement for airplanes and helicopters.
--
-- This is a class used in the @{AI.AI_A2G_Dispatcher}.
--
-- ===
--
-- ### Author: **FlightControl**
--
-- ===
--
-- @module AI.AI_A2G_CAS
-- @image AI_Air_To_Ground_Engage.JPG
-- @type AI_A2G_CAS
-- @extends AI.AI_A2G_Patrol#AI_AIR_PATROL TODO: Documentation. This class does not exist, unable to determine what it extends.
--- Implements the core functions to intercept intruders. Use the Engage trigger to intercept intruders.
--
-- # Developer Note
--
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
-- Therefore, this class is considered to be deprecated
--
-- ===
--
-- @field #AI_A2G_CAS
AI_A2G_CAS = {
ClassName = "AI_A2G_CAS",
}
--- Creates a new AI_A2G_CAS object
-- @param #AI_A2G_CAS self
-- @param Wrapper.Group#GROUP AIGroup
-- @param DCS#Speed EngageMinSpeed The minimum speed of the @{Wrapper.Group} in km/h when engaging a target.
-- @param DCS#Speed EngageMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h when engaging a target.
-- @param DCS#Altitude EngageFloorAltitude The lowest altitude in meters where to execute the engagement.
-- @param DCS#Altitude EngageCeilingAltitude The highest altitude in meters where to execute the engagement.
-- @param DCS#AltitudeType EngageAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to "RADIO".
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Core.Zone} where the patrol needs to be executed.
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h.
-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h.
-- @param DCS#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO
-- @return #AI_A2G_CAS
function AI_A2G_CAS:New2( AIGroup, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType )
local AI_Air = AI_AIR:New( AIGroup )
local AI_Air_Patrol = AI_AIR_PATROL:New( AI_Air, AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType ) -- #AI_AIR_PATROL
local AI_Air_Engage = AI_AIR_ENGAGE:New( AI_Air_Patrol, AIGroup, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType )
local self = BASE:Inherit( self, AI_Air_Engage )
return self
end
--- Creates a new AI_A2G_CAS object
-- @param #AI_A2G_CAS self
-- @param Wrapper.Group#GROUP AIGroup
-- @param DCS#Speed EngageMinSpeed The minimum speed of the @{Wrapper.Group} in km/h when engaging a target.
-- @param DCS#Speed EngageMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h when engaging a target.
-- @param DCS#Altitude EngageFloorAltitude The lowest altitude in meters where to execute the engagement.
-- @param DCS#Altitude EngageCeilingAltitude The highest altitude in meters where to execute the engagement.
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Core.Zone} where the patrol needs to be executed.
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h.
-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h.
-- @param DCS#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO
-- @return #AI_A2G_CAS
function AI_A2G_CAS:New( AIGroup, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType )
return self:New2( AIGroup, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, PatrolAltType, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType)
end
--- Evaluate the attack and create an AttackUnitTask list.
-- @param #AI_A2G_CAS self
-- @param Core.Set#SET_UNIT AttackSetUnit The set of units to attack.
-- @param Wrapper.Group#GROUP DefenderGroup The group of defenders.
-- @param #number EngageAltitude The altitude to engage the targets.
-- @return #AI_A2G_CAS self
function AI_A2G_CAS:CreateAttackUnitTasks( AttackSetUnit, DefenderGroup, EngageAltitude )
local AttackUnitTasks = {}
local AttackSetUnitPerThreatLevel = AttackSetUnit:GetSetPerThreatLevel( 10, 0 )
for AttackUnitIndex, AttackUnit in ipairs( AttackSetUnitPerThreatLevel or {} ) do
if AttackUnit then
if AttackUnit:IsAlive() and AttackUnit:IsGround() then
self:T( { "CAS Unit:", AttackUnit:GetName() } )
AttackUnitTasks[#AttackUnitTasks+1] = DefenderGroup:TaskAttackUnit( AttackUnit, true, false, nil, nil, EngageAltitude )
end
end
end
return AttackUnitTasks
end

File diff suppressed because it is too large Load Diff

View File

@@ -1,131 +0,0 @@
--- **AI** - Models the process of air to ground SEAD engagement for airplanes and helicopters.
--
-- This is a class used in the @{AI.AI_A2G_Dispatcher}.
--
-- ===
--
-- ### Author: **FlightControl**
--
-- ===
--
-- @module AI.AI_A2G_SEAD
-- @image AI_Air_To_Ground_Engage.JPG
-- @type AI_A2G_SEAD
-- @extends AI.AI_A2G_Patrol#AI_AIR_PATROL
--- Implements the core functions to SEAD intruders. Use the Engage trigger to intercept intruders.
--
-- The AI_A2G_SEAD is assigned a @{Wrapper.Group} and this must be done before the AI_A2G_SEAD process can be started using the **Start** event.
--
-- The AI will fly towards the random 3D point within the patrol zone, using a random speed within the given altitude and speed limits.
-- Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits.
--
-- This cycle will continue.
--
-- During the patrol, the AI will detect enemy targets, which are reported through the **Detected** event.
--
-- When enemies are detected, the AI will automatically engage the enemy.
--
-- Until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
-- When the fuel threshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
--
-- ## 1. AI_A2G_SEAD constructor
--
-- * @{#AI_A2G_SEAD.New}(): Creates a new AI_A2G_SEAD object.
--
-- ## 3. Set the Range of Engagement
--
-- An optional range can be set in meters,
-- that will define when the AI will engage with the detected airborne enemy targets.
-- The range can be beyond or smaller than the range of the Patrol Zone.
-- The range is applied at the position of the AI.
-- Use the method @{#AI_AIR_PATROL.SetEngageRange}() to define that range.
--
-- # Developer Note
--
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
-- Therefore, this class is considered to be deprecated
--
-- ===
--
-- @field #AI_A2G_SEAD
AI_A2G_SEAD = {
ClassName = "AI_A2G_SEAD",
}
--- Creates a new AI_A2G_SEAD object
-- @param #AI_A2G_SEAD self
-- @param Wrapper.Group#GROUP AIGroup
-- @param DCS#Speed EngageMinSpeed The minimum speed of the @{Wrapper.Group} in km/h when engaging a target.
-- @param DCS#Speed EngageMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h when engaging a target.
-- @param DCS#Altitude EngageFloorAltitude The lowest altitude in meters where to execute the engagement.
-- @param DCS#Altitude EngageCeilingAltitude The highest altitude in meters where to execute the engagement.
-- @param DCS#AltitudeType EngageAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to "RADIO".
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Core.Zone} where the patrol needs to be executed.
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h.
-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h.
-- @param DCS#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO
-- @return #AI_A2G_SEAD
function AI_A2G_SEAD:New2( AIGroup, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType )
local AI_Air = AI_AIR:New( AIGroup )
local AI_Air_Patrol = AI_AIR_PATROL:New( AI_Air, AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType )
local AI_Air_Engage = AI_AIR_ENGAGE:New( AI_Air_Patrol, AIGroup, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType )
local self = BASE:Inherit( self, AI_Air_Engage )
return self
end
--- Creates a new AI_A2G_SEAD object
-- @param #AI_A2G_SEAD self
-- @param Wrapper.Group#GROUP AIGroup
-- @param DCS#Speed EngageMinSpeed The minimum speed of the @{Wrapper.Group} in km/h when engaging a target.
-- @param DCS#Speed EngageMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h when engaging a target.
-- @param DCS#Altitude EngageFloorAltitude The lowest altitude in meters where to execute the engagement.
-- @param DCS#Altitude EngageCeilingAltitude The highest altitude in meters where to execute the engagement.
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Core.Zone} where the patrol needs to be executed.
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h.
-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h.
-- @param DCS#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO
-- @return #AI_A2G_SEAD
function AI_A2G_SEAD:New( AIGroup, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType )
return self:New2( AIGroup, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, PatrolAltType, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType )
end
--- Evaluate the attack and create an AttackUnitTask list.
-- @param #AI_A2G_SEAD self
-- @param Core.Set#SET_UNIT AttackSetUnit The set of units to attack.
-- @param Wrapper.Group#GROUP DefenderGroup The group of defenders.
-- @param #number EngageAltitude The altitude to engage the targets.
-- @return #AI_A2G_SEAD self
function AI_A2G_SEAD:CreateAttackUnitTasks( AttackSetUnit, DefenderGroup, EngageAltitude )
local AttackUnitTasks = {}
local AttackSetUnitPerThreatLevel = AttackSetUnit:GetSetPerThreatLevel( 10, 0 )
for AttackUnitID, AttackUnit in ipairs( AttackSetUnitPerThreatLevel ) do
if AttackUnit then
if AttackUnit:IsAlive() and AttackUnit:IsGround() then
local HasRadar = AttackUnit:HasSEAD()
if HasRadar then
self:F( { "SEAD Unit:", AttackUnit:GetName() } )
AttackUnitTasks[#AttackUnitTasks+1] = DefenderGroup:TaskAttackUnit( AttackUnit, true, false, nil, nil, EngageAltitude )
end
end
end
end
return AttackUnitTasks
end

View File

@@ -1,840 +0,0 @@
--- **AI** - Models the process of AI air operations.
--
-- ===
--
-- ### Author: **FlightControl**
--
-- ===
--
-- @module AI.AI_Air
-- @image MOOSE.JPG
---
-- @type AI_AIR
-- @extends Core.Fsm#FSM_CONTROLLABLE
--- The AI_AIR class implements the core functions to operate an AI @{Wrapper.Group}.
--
--
-- # 1) AI_AIR constructor
--
-- * @{#AI_AIR.New}(): Creates a new AI_AIR object.
--
-- # 2) AI_AIR is a Finite State Machine.
--
-- This section must be read as follows. Each of the rows indicate a state transition, triggered through an event, and with an ending state of the event was executed.
-- The first column is the **From** state, the second column the **Event**, and the third column the **To** state.
--
-- So, each of the rows have the following structure.
--
-- * **From** => **Event** => **To**
--
-- Important to know is that an event can only be executed if the **current state** is the **From** state.
-- This, when an **Event** that is being triggered has a **From** state that is equal to the **Current** state of the state machine, the event will be executed,
-- and the resulting state will be the **To** state.
--
-- These are the different possible state transitions of this state machine implementation:
--
-- * Idle => Start => Monitoring
--
-- ## 2.1) AI_AIR States.
--
-- * **Idle**: The process is idle.
--
-- ## 2.2) AI_AIR Events.
--
-- * **Start**: Start the transport process.
-- * **Stop**: Stop the transport process.
-- * **Monitor**: Monitor and take action.
--
-- # Developer Note
--
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
-- Therefore, this class is considered to be deprecated
--
-- @field #AI_AIR
AI_AIR = {
ClassName = "AI_AIR",
}
AI_AIR.TaskDelay = 0.5 -- The delay of each task given to the AI.
--- Creates a new AI_AIR process.
-- @param #AI_AIR self
-- @param Wrapper.Group#GROUP AIGroup The group object to receive the A2G Process.
-- @return #AI_AIR
function AI_AIR:New( AIGroup )
-- Inherits from BASE
local self = BASE:Inherit( self, FSM_CONTROLLABLE:New() ) -- #AI_AIR
self:SetControllable( AIGroup )
self:SetStartState( "Stopped" )
self:AddTransition( "*", "Queue", "Queued" )
self:AddTransition( "*", "Start", "Started" )
--- Start Handler OnBefore for AI_AIR
-- @function [parent=#AI_AIR] OnBeforeStart
-- @param #AI_AIR self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @return #boolean
--- Start Handler OnAfter for AI_AIR
-- @function [parent=#AI_AIR] OnAfterStart
-- @param #AI_AIR self
-- @param #string From
-- @param #string Event
-- @param #string To
--- Start Trigger for AI_AIR
-- @function [parent=#AI_AIR] Start
-- @param #AI_AIR self
--- Start Asynchronous Trigger for AI_AIR
-- @function [parent=#AI_AIR] __Start
-- @param #AI_AIR self
-- @param #number Delay
self:AddTransition( "*", "Stop", "Stopped" )
--- OnLeave Transition Handler for State Stopped.
-- @function [parent=#AI_AIR] OnLeaveStopped
-- @param #AI_AIR self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnEnter Transition Handler for State Stopped.
-- @function [parent=#AI_AIR] OnEnterStopped
-- @param #AI_AIR self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- OnBefore Transition Handler for Event Stop.
-- @function [parent=#AI_AIR] OnBeforeStop
-- @param #AI_AIR self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Stop.
-- @function [parent=#AI_AIR] OnAfterStop
-- @param #AI_AIR self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Stop.
-- @function [parent=#AI_AIR] Stop
-- @param #AI_AIR self
--- Asynchronous Event Trigger for Event Stop.
-- @function [parent=#AI_AIR] __Stop
-- @param #AI_AIR self
-- @param #number Delay The delay in seconds.
self:AddTransition( "*", "Status", "*" ) -- FSM_CONTROLLABLE Transition for type #AI_AIR.
--- OnBefore Transition Handler for Event Status.
-- @function [parent=#AI_AIR] OnBeforeStatus
-- @param #AI_AIR self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Status.
-- @function [parent=#AI_AIR] OnAfterStatus
-- @param #AI_AIR self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Status.
-- @function [parent=#AI_AIR] Status
-- @param #AI_AIR self
--- Asynchronous Event Trigger for Event Status.
-- @function [parent=#AI_AIR] __Status
-- @param #AI_AIR self
-- @param #number Delay The delay in seconds.
self:AddTransition( "*", "RTB", "*" ) -- FSM_CONTROLLABLE Transition for type #AI_AIR.
--- OnBefore Transition Handler for Event RTB.
-- @function [parent=#AI_AIR] OnBeforeRTB
-- @param #AI_AIR self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event RTB.
-- @function [parent=#AI_AIR] OnAfterRTB
-- @param #AI_AIR self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event RTB.
-- @function [parent=#AI_AIR] RTB
-- @param #AI_AIR self
--- Asynchronous Event Trigger for Event RTB.
-- @function [parent=#AI_AIR] __RTB
-- @param #AI_AIR self
-- @param #number Delay The delay in seconds.
--- OnLeave Transition Handler for State Returning.
-- @function [parent=#AI_AIR] OnLeaveReturning
-- @param #AI_AIR self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnEnter Transition Handler for State Returning.
-- @function [parent=#AI_AIR] OnEnterReturning
-- @param #AI_AIR self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
self:AddTransition( "Patrolling", "Refuel", "Refuelling" )
--- Refuel Handler OnBefore for AI_AIR
-- @function [parent=#AI_AIR] OnBeforeRefuel
-- @param #AI_AIR self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From
-- @param #string Event
-- @param #string To
-- @return #boolean
--- Refuel Handler OnAfter for AI_AIR
-- @function [parent=#AI_AIR] OnAfterRefuel
-- @param #AI_AIR self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From
-- @param #string Event
-- @param #string To
--- Refuel Trigger for AI_AIR
-- @function [parent=#AI_AIR] Refuel
-- @param #AI_AIR self
--- Refuel Asynchronous Trigger for AI_AIR
-- @function [parent=#AI_AIR] __Refuel
-- @param #AI_AIR self
-- @param #number Delay
self:AddTransition( "*", "Takeoff", "Airborne" )
self:AddTransition( "*", "Return", "Returning" )
self:AddTransition( "*", "Hold", "Holding" )
self:AddTransition( "*", "Home", "Home" )
self:AddTransition( "*", "LostControl", "LostControl" )
self:AddTransition( "*", "Fuel", "Fuel" )
self:AddTransition( "*", "Damaged", "Damaged" )
self:AddTransition( "*", "Eject", "*" )
self:AddTransition( "*", "Crash", "Crashed" )
self:AddTransition( "*", "PilotDead", "*" )
self.IdleCount = 0
self.RTBSpeedMaxFactor = 0.6
self.RTBSpeedMinFactor = 0.5
return self
end
-- @param Wrapper.Group#GROUP self
-- @param Core.Event#EVENTDATA EventData
function GROUP:OnEventTakeoff( EventData, Fsm )
Fsm:Takeoff()
self:UnHandleEvent( EVENTS.Takeoff )
end
function AI_AIR:SetDispatcher( Dispatcher )
self.Dispatcher = Dispatcher
end
function AI_AIR:GetDispatcher()
return self.Dispatcher
end
function AI_AIR:SetTargetDistance( Coordinate )
local CurrentCoord = self.Controllable:GetCoordinate()
self.TargetDistance = CurrentCoord:Get2DDistance( Coordinate )
self.ClosestTargetDistance = ( not self.ClosestTargetDistance or self.ClosestTargetDistance > self.TargetDistance ) and self.TargetDistance or self.ClosestTargetDistance
end
function AI_AIR:ClearTargetDistance()
self.TargetDistance = nil
self.ClosestTargetDistance = nil
end
--- Sets (modifies) the minimum and maximum speed of the patrol.
-- @param #AI_AIR self
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Controllable} in km/h.
-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Controllable} in km/h.
-- @return #AI_AIR self
function AI_AIR:SetSpeed( PatrolMinSpeed, PatrolMaxSpeed )
self:F2( { PatrolMinSpeed, PatrolMaxSpeed } )
self.PatrolMinSpeed = PatrolMinSpeed
self.PatrolMaxSpeed = PatrolMaxSpeed
end
--- Sets (modifies) the minimum and maximum RTB speed of the patrol.
-- @param #AI_AIR self
-- @param DCS#Speed RTBMinSpeed The minimum speed of the @{Wrapper.Controllable} in km/h.
-- @param DCS#Speed RTBMaxSpeed The maximum speed of the @{Wrapper.Controllable} in km/h.
-- @return #AI_AIR self
function AI_AIR:SetRTBSpeed( RTBMinSpeed, RTBMaxSpeed )
self:F( { RTBMinSpeed, RTBMaxSpeed } )
self.RTBMinSpeed = RTBMinSpeed
self.RTBMaxSpeed = RTBMaxSpeed
end
--- Sets the floor and ceiling altitude of the patrol.
-- @param #AI_AIR self
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
-- @return #AI_AIR self
function AI_AIR:SetAltitude( PatrolFloorAltitude, PatrolCeilingAltitude )
self:F2( { PatrolFloorAltitude, PatrolCeilingAltitude } )
self.PatrolFloorAltitude = PatrolFloorAltitude
self.PatrolCeilingAltitude = PatrolCeilingAltitude
end
--- Sets the home airbase.
-- @param #AI_AIR self
-- @param Wrapper.Airbase#AIRBASE HomeAirbase
-- @return #AI_AIR self
function AI_AIR:SetHomeAirbase( HomeAirbase )
self:F2( { HomeAirbase } )
self.HomeAirbase = HomeAirbase
end
--- Sets to refuel at the given tanker.
-- @param #AI_AIR self
-- @param Wrapper.Group#GROUP TankerName The group name of the tanker as defined within the Mission Editor or spawned.
-- @return #AI_AIR self
function AI_AIR:SetTanker( TankerName )
self:F2( { TankerName } )
self.TankerName = TankerName
end
--- Sets the disengage range, that when engaging a target beyond the specified range, the engagement will be cancelled and the plane will RTB.
-- @param #AI_AIR self
-- @param #number DisengageRadius The disengage range.
-- @return #AI_AIR self
function AI_AIR:SetDisengageRadius( DisengageRadius )
self:F2( { DisengageRadius } )
self.DisengageRadius = DisengageRadius
end
--- Set the status checking off.
-- @param #AI_AIR self
-- @return #AI_AIR self
function AI_AIR:SetStatusOff()
self:F2()
self.CheckStatus = false
end
--- When the AI is out of fuel, it is required that a new AI is started, before the old AI can return to the home base.
-- Therefore, with a parameter and a calculation of the distance to the home base, the fuel threshold is calculated.
-- When the fuel threshold is reached, the AI will continue for a given time its patrol task in orbit, while a new AIControllable is targeted to the AI_AIR.
-- Once the time is finished, the old AI will return to the base.
-- @param #AI_AIR self
-- @param #number FuelThresholdPercentage The threshold in percentage (between 0 and 1) when the AIControllable is considered to get out of fuel.
-- @param #number OutOfFuelOrbitTime The amount of seconds the out of fuel AIControllable will orbit before returning to the base.
-- @return #AI_AIR self
function AI_AIR:SetFuelThreshold( FuelThresholdPercentage, OutOfFuelOrbitTime )
self.FuelThresholdPercentage = FuelThresholdPercentage
self.OutOfFuelOrbitTime = OutOfFuelOrbitTime
self.Controllable:OptionRTBBingoFuel( false )
return self
end
--- When the AI is damaged beyond a certain threshold, it is required that the AI returns to the home base.
-- However, damage cannot be foreseen early on.
-- Therefore, when the damage threshold is reached,
-- the AI will return immediately to the home base (RTB).
-- Note that for groups, the average damage of the complete group will be calculated.
-- So, in a group of 4 airplanes, 2 lost and 2 with damage 0.2, the damage threshold will be 0.25.
-- @param #AI_AIR self
-- @param #number PatrolDamageThreshold The threshold in percentage (between 0 and 1) when the AI is considered to be damaged.
-- @return #AI_AIR self
function AI_AIR:SetDamageThreshold( PatrolDamageThreshold )
self.PatrolManageDamage = true
self.PatrolDamageThreshold = PatrolDamageThreshold
return self
end
--- Defines a new patrol route using the @{AI.AI_Patrol#AI_PATROL_ZONE} parameters and settings.
-- @param #AI_AIR self
-- @return #AI_AIR self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_AIR:onafterStart( Controllable, From, Event, To )
self:__Status( 10 ) -- Check status status every 30 seconds.
self:HandleEvent( EVENTS.PilotDead, self.OnPilotDead )
self:HandleEvent( EVENTS.Crash, self.OnCrash )
self:HandleEvent( EVENTS.Ejection, self.OnEjection )
Controllable:OptionROEHoldFire()
Controllable:OptionROTVertical()
end
--- Coordinates the approriate returning action.
-- @param #AI_AIR self
-- @return #AI_AIR self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_AIR:onafterReturn( Controllable, From, Event, To )
self:__RTB( self.TaskDelay )
end
-- @param #AI_AIR self
function AI_AIR:onbeforeStatus()
return self.CheckStatus
end
-- @param #AI_AIR self
function AI_AIR:onafterStatus()
if self.Controllable and self.Controllable:IsAlive() then
local RTB = false
local DistanceFromHomeBase = self.HomeAirbase:GetCoordinate():Get2DDistance( self.Controllable:GetCoordinate() )
if not self:Is( "Holding" ) and not self:Is( "Returning" ) then
local DistanceFromHomeBase = self.HomeAirbase:GetCoordinate():Get2DDistance( self.Controllable:GetCoordinate() )
if DistanceFromHomeBase > self.DisengageRadius then
self:T( self.Controllable:GetName() .. " is too far from home base, RTB!" )
self:Hold( 300 )
RTB = false
end
end
-- I think this code is not requirement anymore after release 2.5.
-- if self:Is( "Fuel" ) or self:Is( "Damaged" ) or self:Is( "LostControl" ) then
-- if DistanceFromHomeBase < 5000 then
-- self:E( self.Controllable:GetName() .. " is near the home base, RTB!" )
-- self:Home( "Destroy" )
-- end
-- end
if not self:Is( "Fuel" ) and not self:Is( "Home" ) and not self:is( "Refuelling" )then
local Fuel = self.Controllable:GetFuelMin()
-- If the fuel in the controllable is below the threshold percentage,
-- then send for refuel in case of a tanker, otherwise RTB.
if Fuel < self.FuelThresholdPercentage then
if self.TankerName then
self:T( self.Controllable:GetName() .. " is out of fuel: " .. Fuel .. " ... Refuelling at Tanker!" )
self:Refuel()
else
self:T( self.Controllable:GetName() .. " is out of fuel: " .. Fuel .. " ... RTB!" )
local OldAIControllable = self.Controllable
local OrbitTask = OldAIControllable:TaskOrbitCircle( math.random( self.PatrolFloorAltitude, self.PatrolCeilingAltitude ), self.PatrolMinSpeed )
local TimedOrbitTask = OldAIControllable:TaskControlled( OrbitTask, OldAIControllable:TaskCondition(nil,nil,nil,nil,self.OutOfFuelOrbitTime,nil ) )
OldAIControllable:SetTask( TimedOrbitTask, 10 )
self:Fuel()
RTB = true
end
else
end
end
if self:Is( "Fuel" ) and not self:Is( "Home" ) and not self:is( "Refuelling" ) then
RTB = true
end
-- TODO: Check GROUP damage function.
local Damage = self.Controllable:GetLife()
local InitialLife = self.Controllable:GetLife0()
-- If the group is damaged, then RTB.
-- Note that a group can consist of more units, so if one unit is damaged of a group, the mission may continue.
-- The damaged unit will RTB due to DCS logic, and the others will continue to engage.
if ( Damage / InitialLife ) < self.PatrolDamageThreshold then
self:T( self.Controllable:GetName() .. " is damaged: " .. Damage .. " ... RTB!" )
self:Damaged()
RTB = true
self:SetStatusOff()
end
-- Check if planes went RTB and are out of control.
-- We only check if planes are out of control, when they are in duty.
if self.Controllable:HasTask() == false then
if not self:Is( "Started" ) and
not self:Is( "Stopped" ) and
not self:Is( "Fuel" ) and
not self:Is( "Damaged" ) and
not self:Is( "Home" ) then
if self.IdleCount >= 10 then
if Damage ~= InitialLife then
self:Damaged()
else
self:T( self.Controllable:GetName() .. " control lost! " )
self:LostControl()
end
else
self.IdleCount = self.IdleCount + 1
end
end
else
self.IdleCount = 0
end
if RTB == true then
self:__RTB( self.TaskDelay )
end
if not self:Is("Home") then
self:__Status( 10 )
end
end
end
-- @param Wrapper.Group#GROUP AIGroup
function AI_AIR.RTBRoute( AIGroup, Fsm )
AIGroup:F( { "AI_AIR.RTBRoute:", AIGroup:GetName() } )
if AIGroup:IsAlive() then
Fsm:RTB()
end
end
-- @param Wrapper.Group#GROUP AIGroup
function AI_AIR.RTBHold( AIGroup, Fsm )
AIGroup:F( { "AI_AIR.RTBHold:", AIGroup:GetName() } )
if AIGroup:IsAlive() then
Fsm:__RTB( Fsm.TaskDelay )
Fsm:Return()
local Task = AIGroup:TaskOrbitCircle( 4000, 400 )
AIGroup:SetTask( Task )
end
end
--- Set the min and max factors on RTB speed. Use this, if your planes are heading back to base too fast. Default values are 0.5 and 0.6.
-- The RTB speed is calculated as the max speed of the unit multiplied by MinFactor (lower bracket) and multiplied by MaxFactor (upper bracket).
-- A random value in this bracket is then applied in the waypoint routing generation.
-- @param #AI_AIR self
-- @param #number MinFactor Lower bracket factor. Defaults to 0.5.
-- @param #number MaxFactor Upper bracket factor. Defaults to 0.6.
-- @return #AI_AIR self
function AI_AIR:SetRTBSpeedFactors(MinFactor,MaxFactor)
self.RTBSpeedMaxFactor = MaxFactor or 0.6
self.RTBSpeedMinFactor = MinFactor or 0.5
return self
end
-- @param #AI_AIR self
-- @param Wrapper.Group#GROUP AIGroup
function AI_AIR:onafterRTB( AIGroup, From, Event, To )
self:F( { AIGroup, From, Event, To } )
if AIGroup and AIGroup:IsAlive() then
self:T( "Group " .. AIGroup:GetName() .. " ... RTB! ( " .. self:GetState() .. " )" )
self:ClearTargetDistance()
--AIGroup:ClearTasks()
AIGroup:OptionProhibitAfterburner(true)
local EngageRoute = {}
--- Calculate the target route point.
local FromCoord = AIGroup:GetCoordinate()
if not FromCoord then return end
local ToTargetCoord = self.HomeAirbase:GetCoordinate() -- coordinate is on land height(!)
local ToTargetVec3 = ToTargetCoord:GetVec3()
ToTargetVec3.y = ToTargetCoord:GetLandHeight()+3000 -- let's set this 1000m/3000 feet above ground
local ToTargetCoord2 = COORDINATE:NewFromVec3( ToTargetVec3 )
if not self.RTBMinSpeed or not self.RTBMaxSpeed then
local RTBSpeedMax = AIGroup:GetSpeedMax()
local RTBSpeedMaxFactor = self.RTBSpeedMaxFactor or 0.6
local RTBSpeedMinFactor = self.RTBSpeedMinFactor or 0.5
self:SetRTBSpeed( RTBSpeedMax * RTBSpeedMinFactor, RTBSpeedMax * RTBSpeedMaxFactor)
end
local RTBSpeed = math.random( self.RTBMinSpeed, self.RTBMaxSpeed )
--local ToAirbaseAngle = FromCoord:GetAngleDegrees( FromCoord:GetDirectionVec3( ToTargetCoord2 ) )
local Distance = FromCoord:Get2DDistance( ToTargetCoord2 )
--local ToAirbaseCoord = FromCoord:Translate( 5000, ToAirbaseAngle )
local ToAirbaseCoord = ToTargetCoord2
if Distance < 5000 then
self:T( "RTB and near the airbase!" )
self:Home()
return
end
if not AIGroup:InAir() == true then
self:T( "Not anymore in the air, considered Home." )
self:Home()
return
end
--- Create a route point of type air.
local FromRTBRoutePoint = FromCoord:WaypointAir(
self.PatrolAltType,
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
RTBSpeed,
true
)
--- Create a route point of type air.
local ToRTBRoutePoint = ToAirbaseCoord:WaypointAir(
self.PatrolAltType,
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
RTBSpeed,
true
)
EngageRoute[#EngageRoute+1] = FromRTBRoutePoint
EngageRoute[#EngageRoute+1] = ToRTBRoutePoint
local Tasks = {}
Tasks[#Tasks+1] = AIGroup:TaskFunction( "AI_AIR.RTBRoute", self )
EngageRoute[#EngageRoute].task = AIGroup:TaskCombo( Tasks )
AIGroup:OptionROEHoldFire()
AIGroup:OptionROTEvadeFire()
--- NOW ROUTE THE GROUP!
AIGroup:Route( EngageRoute, self.TaskDelay )
end
end
-- @param #AI_AIR self
-- @param Wrapper.Group#GROUP AIGroup
function AI_AIR:onafterHome( AIGroup, From, Event, To )
self:F( { AIGroup, From, Event, To } )
self:T( "Group " .. self.Controllable:GetName() .. " ... Home! ( " .. self:GetState() .. " )" )
if AIGroup and AIGroup:IsAlive() then
end
end
-- @param #AI_AIR self
-- @param Wrapper.Group#GROUP AIGroup
function AI_AIR:onafterHold( AIGroup, From, Event, To, HoldTime )
self:F( { AIGroup, From, Event, To } )
self:T( "Group " .. self.Controllable:GetName() .. " ... Holding! ( " .. self:GetState() .. " )" )
if AIGroup and AIGroup:IsAlive() then
local Coordinate = AIGroup:GetCoordinate()
if Coordinate == nil then return end
local OrbitTask = AIGroup:TaskOrbitCircle( math.random( self.PatrolFloorAltitude, self.PatrolCeilingAltitude ), self.PatrolMinSpeed, Coordinate )
local TimedOrbitTask = AIGroup:TaskControlled( OrbitTask, AIGroup:TaskCondition( nil, nil, nil, nil, HoldTime , nil ) )
local RTBTask = AIGroup:TaskFunction( "AI_AIR.RTBHold", self )
local OrbitHoldTask = AIGroup:TaskOrbitCircle( 4000, self.PatrolMinSpeed )
--AIGroup:SetState( AIGroup, "AI_AIR", self )
AIGroup:SetTask( AIGroup:TaskCombo( { TimedOrbitTask, RTBTask, OrbitHoldTask } ), 1 )
end
end
-- @param Wrapper.Group#GROUP AIGroup
function AI_AIR.Resume( AIGroup, Fsm )
AIGroup:T( { "AI_AIR.Resume:", AIGroup:GetName() } )
if AIGroup:IsAlive() then
Fsm:__RTB( Fsm.TaskDelay )
end
end
-- @param #AI_AIR self
-- @param Wrapper.Group#GROUP AIGroup
function AI_AIR:onafterRefuel( AIGroup, From, Event, To )
self:F( { AIGroup, From, Event, To } )
if AIGroup and AIGroup:IsAlive() then
-- Get tanker group.
local Tanker = GROUP:FindByName( self.TankerName )
if Tanker and Tanker:IsAlive() and Tanker:IsAirPlane() then
self:T( "Group " .. self.Controllable:GetName() .. " ... Refuelling! State=" .. self:GetState() .. ", Refuelling tanker " .. self.TankerName )
local RefuelRoute = {}
--- Calculate the target route point.
local FromRefuelCoord = AIGroup:GetCoordinate()
local ToRefuelCoord = Tanker:GetCoordinate()
local ToRefuelSpeed = math.random( self.PatrolMinSpeed, self.PatrolMaxSpeed )
--- Create a route point of type air.
local FromRefuelRoutePoint = FromRefuelCoord:WaypointAir(self.PatrolAltType, POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, ToRefuelSpeed, true)
--- Create a route point of type air. NOT used!
local ToRefuelRoutePoint = Tanker:GetCoordinate():WaypointAir(self.PatrolAltType, POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, ToRefuelSpeed, true)
self:F( { ToRefuelSpeed = ToRefuelSpeed } )
RefuelRoute[#RefuelRoute+1] = FromRefuelRoutePoint
RefuelRoute[#RefuelRoute+1] = ToRefuelRoutePoint
AIGroup:OptionROEHoldFire()
AIGroup:OptionROTEvadeFire()
-- Get Class name for .Resume function
local classname=self:GetClassName()
-- AI_A2A_CAP can call this function but does not have a .Resume function. Try to fix.
if classname=="AI_A2A_CAP" then
classname="AI_AIR_PATROL"
end
env.info("FF refueling classname="..classname)
local Tasks = {}
Tasks[#Tasks+1] = AIGroup:TaskRefueling()
Tasks[#Tasks+1] = AIGroup:TaskFunction( classname .. ".Resume", self )
RefuelRoute[#RefuelRoute].task = AIGroup:TaskCombo( Tasks )
AIGroup:Route( RefuelRoute, self.TaskDelay )
else
-- No tanker defined ==> RTB!
self:RTB()
end
end
end
-- @param #AI_AIR self
function AI_AIR:onafterDead()
self:SetStatusOff()
end
-- @param #AI_AIR self
-- @param Core.Event#EVENTDATA EventData
function AI_AIR:OnCrash( EventData )
if self.Controllable:IsAlive() and EventData.IniDCSGroupName == self.Controllable:GetName() then
if #self.Controllable:GetUnits() == 1 then
self:__Crash( self.TaskDelay, EventData )
end
end
end
-- @param #AI_AIR self
-- @param Core.Event#EVENTDATA EventData
function AI_AIR:OnEjection( EventData )
if self.Controllable:IsAlive() and EventData.IniDCSGroupName == self.Controllable:GetName() then
self:__Eject( self.TaskDelay, EventData )
end
end
-- @param #AI_AIR self
-- @param Core.Event#EVENTDATA EventData
function AI_AIR:OnPilotDead( EventData )
if self.Controllable:IsAlive() and EventData.IniDCSGroupName == self.Controllable:GetName() then
self:__PilotDead( self.TaskDelay, EventData )
end
end

File diff suppressed because it is too large Load Diff

View File

@@ -1,593 +0,0 @@
--- **AI** - Models the process of air to ground engagement for airplanes and helicopters.
--
-- This is a class used in the @{AI.AI_A2G_Dispatcher}.
--
-- ===
--
-- ### Author: **FlightControl**
--
-- ===
--
-- @module AI.AI_Air_Engage
-- @image AI_Air_To_Ground_Engage.JPG
-- @type AI_AIR_ENGAGE
-- @extends AI.AI_AIR#AI_AIR
--- Implements the core functions to intercept intruders. Use the Engage trigger to intercept intruders.
--
-- The AI_AIR_ENGAGE is assigned a @{Wrapper.Group} and this must be done before the AI_AIR_ENGAGE process can be started using the **Start** event.
--
-- The AI will fly towards the random 3D point within the patrol zone, using a random speed within the given altitude and speed limits.
-- Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits.
--
-- This cycle will continue.
--
-- During the patrol, the AI will detect enemy targets, which are reported through the **Detected** event.
--
-- When enemies are detected, the AI will automatically engage the enemy.
--
-- Until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
-- When the fuel threshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
--
-- ## 1. AI_AIR_ENGAGE constructor
--
-- * @{#AI_AIR_ENGAGE.New}(): Creates a new AI_AIR_ENGAGE object.
--
-- ## 2. Set the Zone of Engagement
--
-- An optional @{Core.Zone} can be set,
-- that will define when the AI will engage with the detected airborne enemy targets.
-- Use the method @{AI.AI_CAP#AI_AIR_ENGAGE.SetEngageZone}() to define that Zone.
--
-- # Developer Note
--
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
-- Therefore, this class is considered to be deprecated
--
-- # Developer Note
--
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
-- Therefore, this class is considered to be deprecated
--
-- ===
--
-- @field #AI_AIR_ENGAGE
AI_AIR_ENGAGE = {
ClassName = "AI_AIR_ENGAGE",
}
--- Creates a new AI_AIR_ENGAGE object
-- @param #AI_AIR_ENGAGE self
-- @param AI.AI_Air#AI_AIR AI_Air The AI_AIR FSM.
-- @param Wrapper.Group#GROUP AIGroup The AI group.
-- @param DCS#Speed EngageMinSpeed (optional, default = 50% of max speed) The minimum speed of the @{Wrapper.Group} in km/h when engaging a target.
-- @param DCS#Speed EngageMaxSpeed (optional, default = 75% of max speed) The maximum speed of the @{Wrapper.Group} in km/h when engaging a target.
-- @param DCS#Altitude EngageFloorAltitude (optional, default = 1000m ) The lowest altitude in meters where to execute the engagement.
-- @param DCS#Altitude EngageCeilingAltitude (optional, default = 1500m ) The highest altitude in meters where to execute the engagement.
-- @param DCS#AltitudeType EngageAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to "RADIO".
-- @return #AI_AIR_ENGAGE
function AI_AIR_ENGAGE:New( AI_Air, AIGroup, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType )
-- Inherits from BASE
local self = BASE:Inherit( self, AI_Air ) -- #AI_AIR_ENGAGE
self.Accomplished = false
self.Engaging = false
local SpeedMax = AIGroup:GetSpeedMax()
self.EngageMinSpeed = EngageMinSpeed or SpeedMax * 0.5
self.EngageMaxSpeed = EngageMaxSpeed or SpeedMax * 0.75
self.EngageFloorAltitude = EngageFloorAltitude or 1000
self.EngageCeilingAltitude = EngageCeilingAltitude or 1500
self.EngageAltType = EngageAltType or "RADIO"
self:AddTransition( { "Started", "Engaging", "Returning", "Airborne", "Patrolling" }, "EngageRoute", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_AIR_ENGAGE.
--- OnBefore Transition Handler for Event EngageRoute.
-- @function [parent=#AI_AIR_ENGAGE] OnBeforeEngageRoute
-- @param #AI_AIR_ENGAGE self
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event EngageRoute.
-- @function [parent=#AI_AIR_ENGAGE] OnAfterEngageRoute
-- @param #AI_AIR_ENGAGE self
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event EngageRoute.
-- @function [parent=#AI_AIR_ENGAGE] EngageRoute
-- @param #AI_AIR_ENGAGE self
--- Asynchronous Event Trigger for Event EngageRoute.
-- @function [parent=#AI_AIR_ENGAGE] __EngageRoute
-- @param #AI_AIR_ENGAGE self
-- @param #number Delay The delay in seconds.
--- OnLeave Transition Handler for State Engaging.
-- @function [parent=#AI_AIR_ENGAGE] OnLeaveEngaging
-- @param #AI_AIR_ENGAGE self
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnEnter Transition Handler for State Engaging.
-- @function [parent=#AI_AIR_ENGAGE] OnEnterEngaging
-- @param #AI_AIR_ENGAGE self
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
self:AddTransition( { "Started", "Engaging", "Returning", "Airborne", "Patrolling" }, "Engage", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_AIR_ENGAGE.
--- OnBefore Transition Handler for Event Engage.
-- @function [parent=#AI_AIR_ENGAGE] OnBeforeEngage
-- @param #AI_AIR_ENGAGE self
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Engage.
-- @function [parent=#AI_AIR_ENGAGE] OnAfterEngage
-- @param #AI_AIR_ENGAGE self
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Engage.
-- @function [parent=#AI_AIR_ENGAGE] Engage
-- @param #AI_AIR_ENGAGE self
--- Asynchronous Event Trigger for Event Engage.
-- @function [parent=#AI_AIR_ENGAGE] __Engage
-- @param #AI_AIR_ENGAGE self
-- @param #number Delay The delay in seconds.
--- OnLeave Transition Handler for State Engaging.
-- @function [parent=#AI_AIR_ENGAGE] OnLeaveEngaging
-- @param #AI_AIR_ENGAGE self
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnEnter Transition Handler for State Engaging.
-- @function [parent=#AI_AIR_ENGAGE] OnEnterEngaging
-- @param #AI_AIR_ENGAGE self
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
self:AddTransition( "Engaging", "Fired", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_AIR_ENGAGE.
--- OnBefore Transition Handler for Event Fired.
-- @function [parent=#AI_AIR_ENGAGE] OnBeforeFired
-- @param #AI_AIR_ENGAGE self
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Fired.
-- @function [parent=#AI_AIR_ENGAGE] OnAfterFired
-- @param #AI_AIR_ENGAGE self
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Fired.
-- @function [parent=#AI_AIR_ENGAGE] Fired
-- @param #AI_AIR_ENGAGE self
--- Asynchronous Event Trigger for Event Fired.
-- @function [parent=#AI_AIR_ENGAGE] __Fired
-- @param #AI_AIR_ENGAGE self
-- @param #number Delay The delay in seconds.
self:AddTransition( "*", "Destroy", "*" ) -- FSM_CONTROLLABLE Transition for type #AI_AIR_ENGAGE.
--- OnBefore Transition Handler for Event Destroy.
-- @function [parent=#AI_AIR_ENGAGE] OnBeforeDestroy
-- @param #AI_AIR_ENGAGE self
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Destroy.
-- @function [parent=#AI_AIR_ENGAGE] OnAfterDestroy
-- @param #AI_AIR_ENGAGE self
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Destroy.
-- @function [parent=#AI_AIR_ENGAGE] Destroy
-- @param #AI_AIR_ENGAGE self
--- Asynchronous Event Trigger for Event Destroy.
-- @function [parent=#AI_AIR_ENGAGE] __Destroy
-- @param #AI_AIR_ENGAGE self
-- @param #number Delay The delay in seconds.
self:AddTransition( "Engaging", "Abort", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_AIR_ENGAGE.
--- OnBefore Transition Handler for Event Abort.
-- @function [parent=#AI_AIR_ENGAGE] OnBeforeAbort
-- @param #AI_AIR_ENGAGE self
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Abort.
-- @function [parent=#AI_AIR_ENGAGE] OnAfterAbort
-- @param #AI_AIR_ENGAGE self
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Abort.
-- @function [parent=#AI_AIR_ENGAGE] Abort
-- @param #AI_AIR_ENGAGE self
--- Asynchronous Event Trigger for Event Abort.
-- @function [parent=#AI_AIR_ENGAGE] __Abort
-- @param #AI_AIR_ENGAGE self
-- @param #number Delay The delay in seconds.
self:AddTransition( "Engaging", "Accomplish", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_AIR_ENGAGE.
--- OnBefore Transition Handler for Event Accomplish.
-- @function [parent=#AI_AIR_ENGAGE] OnBeforeAccomplish
-- @param #AI_AIR_ENGAGE self
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Accomplish.
-- @function [parent=#AI_AIR_ENGAGE] OnAfterAccomplish
-- @param #AI_AIR_ENGAGE self
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Accomplish.
-- @function [parent=#AI_AIR_ENGAGE] Accomplish
-- @param #AI_AIR_ENGAGE self
--- Asynchronous Event Trigger for Event Accomplish.
-- @function [parent=#AI_AIR_ENGAGE] __Accomplish
-- @param #AI_AIR_ENGAGE self
-- @param #number Delay The delay in seconds.
self:AddTransition( { "Patrolling", "Engaging" }, "Refuel", "Refuelling" )
return self
end
--- onafter event handler for Start event.
-- @param #AI_AIR_ENGAGE self
-- @param Wrapper.Group#GROUP AIGroup The AI group managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_AIR_ENGAGE:onafterStart( AIGroup, From, Event, To )
self:GetParent( self, AI_AIR_ENGAGE ).onafterStart( self, AIGroup, From, Event, To )
AIGroup:HandleEvent( EVENTS.Takeoff, nil, self )
end
--- onafter event handler for Engage event.
-- @param #AI_AIR_ENGAGE self
-- @param Wrapper.Group#GROUP AIGroup The AI Group managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_AIR_ENGAGE:onafterEngage( AIGroup, From, Event, To )
-- TODO: This function is overwritten below!
self:HandleEvent( EVENTS.Dead )
end
-- todo: need to fix this global function
--- onbefore event handler for Engage event.
-- @param #AI_AIR_ENGAGE self
-- @param Wrapper.Group#GROUP AIGroup The group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_AIR_ENGAGE:onbeforeEngage( AIGroup, From, Event, To )
if self.Accomplished == true then
return false
end
return true
end
--- onafter event handler for Abort event.
-- @param #AI_AIR_ENGAGE self
-- @param Wrapper.Group#GROUP AIGroup The AI Group managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_AIR_ENGAGE:onafterAbort( AIGroup, From, Event, To )
AIGroup:ClearTasks()
self:Return()
end
-- @param #AI_AIR_ENGAGE self
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_AIR_ENGAGE:onafterAccomplish( AIGroup, From, Event, To )
self.Accomplished = true
--self:SetDetectionOff()
end
-- @param #AI_AIR_ENGAGE self
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @param Core.Event#EVENTDATA EventData
function AI_AIR_ENGAGE:onafterDestroy( AIGroup, From, Event, To, EventData )
if EventData.IniUnit then
self.AttackUnits[EventData.IniUnit] = nil
end
end
-- @param #AI_AIR_ENGAGE self
-- @param Core.Event#EVENTDATA EventData
function AI_AIR_ENGAGE:OnEventDead( EventData )
self:F( { "EventDead", EventData } )
if EventData.IniDCSUnit then
if self.AttackUnits and self.AttackUnits[EventData.IniUnit] then
self:__Destroy( self.TaskDelay, EventData )
end
end
end
-- @param Wrapper.Group#GROUP AIControllable
function AI_AIR_ENGAGE.___EngageRoute( AIGroup, Fsm, AttackSetUnit )
Fsm:T(string.format("AI_AIR_ENGAGE.___EngageRoute: %s", tostring(AIGroup:GetName())))
if AIGroup and AIGroup:IsAlive() then
Fsm:__EngageRoute( Fsm.TaskDelay or 0.1, AttackSetUnit )
end
end
-- @param #AI_AIR_ENGAGE self
-- @param Wrapper.Group#GROUP DefenderGroup The GroupGroup managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @param Core.Set#SET_UNIT AttackSetUnit Unit set to be attacked.
function AI_AIR_ENGAGE:onafterEngageRoute( DefenderGroup, From, Event, To, AttackSetUnit )
self:T( { DefenderGroup, From, Event, To, AttackSetUnit } )
local DefenderGroupName = DefenderGroup:GetName()
self.AttackSetUnit = AttackSetUnit -- Kept in memory in case of resume from refuel in air!
local AttackCount = AttackSetUnit:CountAlive()
if AttackCount > 0 then
if DefenderGroup:IsAlive() then
local EngageAltitude = math.random( self.EngageFloorAltitude, self.EngageCeilingAltitude )
local EngageSpeed = math.random( self.EngageMinSpeed, self.EngageMaxSpeed )
-- Determine the distance to the target.
-- If it is less than 10km, then attack without a route.
-- Otherwise perform a route attack.
local DefenderCoord = DefenderGroup:GetPointVec3()
DefenderCoord:SetY( EngageAltitude ) -- Ground targets don't have an altitude.
local TargetCoord = AttackSetUnit:GetRandomSurely():GetPointVec3()
if TargetCoord == nil then
self:Return()
return
end
TargetCoord:SetY( EngageAltitude ) -- Ground targets don't have an altitude.
local TargetDistance = DefenderCoord:Get2DDistance( TargetCoord )
local EngageDistance = ( DefenderGroup:IsHelicopter() and 5000 ) or ( DefenderGroup:IsAirPlane() and 10000 )
-- TODO: A factor of * 3 is way too close. This causes the AI not to engange until merged sometimes!
if TargetDistance <= EngageDistance * 9 then
--self:T(string.format("AI_AIR_ENGAGE onafterEngageRoute ==> __Engage - target distance = %.1f km", TargetDistance/1000))
self:__Engage( 0.1, AttackSetUnit )
else
--self:T(string.format("FF AI_AIR_ENGAGE onafterEngageRoute ==> Routing - target distance = %.1f km", TargetDistance/1000))
local EngageRoute = {}
local AttackTasks = {}
--- Calculate the target route point.
local FromWP = DefenderCoord:WaypointAir(self.PatrolAltType or "RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, EngageSpeed, true)
EngageRoute[#EngageRoute+1] = FromWP
self:SetTargetDistance( TargetCoord ) -- For RTB status check
local FromEngageAngle = DefenderCoord:GetAngleDegrees( DefenderCoord:GetDirectionVec3( TargetCoord ) )
local ToCoord=DefenderCoord:Translate( EngageDistance, FromEngageAngle, true )
local ToWP = ToCoord:WaypointAir(self.PatrolAltType or "RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, EngageSpeed, true)
EngageRoute[#EngageRoute+1] = ToWP
AttackTasks[#AttackTasks+1] = DefenderGroup:TaskFunction( "AI_AIR_ENGAGE.___EngageRoute", self, AttackSetUnit )
EngageRoute[#EngageRoute].task = DefenderGroup:TaskCombo( AttackTasks )
DefenderGroup:OptionROEReturnFire()
DefenderGroup:OptionROTEvadeFire()
DefenderGroup:Route( EngageRoute, self.TaskDelay or 0.1 )
end
end
else
-- TODO: This will make an A2A Dispatcher CAP flight to return rather than going back to patrolling!
self:T( DefenderGroupName .. ": No targets found -> Going RTB")
self:Return()
end
end
-- @param Wrapper.Group#GROUP AIControllable
function AI_AIR_ENGAGE.___Engage( AIGroup, Fsm, AttackSetUnit )
Fsm:T(string.format("AI_AIR_ENGAGE.___Engage: %s", tostring(AIGroup:GetName())))
if AIGroup and AIGroup:IsAlive() then
local delay=Fsm.TaskDelay or 0.1
Fsm:__Engage(delay, AttackSetUnit)
end
end
-- @param #AI_AIR_ENGAGE self
-- @param Wrapper.Group#GROUP DefenderGroup The GroupGroup managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @param Core.Set#SET_UNIT AttackSetUnit Set of units to be attacked.
function AI_AIR_ENGAGE:onafterEngage( DefenderGroup, From, Event, To, AttackSetUnit )
self:F( { DefenderGroup, From, Event, To, AttackSetUnit} )
local DefenderGroupName = DefenderGroup:GetName()
self.AttackSetUnit = AttackSetUnit -- Kept in memory in case of resume from refuel in air!
local AttackCount = AttackSetUnit:CountAlive()
self:T({AttackCount = AttackCount})
if AttackCount > 0 then
if DefenderGroup and DefenderGroup:IsAlive() then
local EngageAltitude = math.random( self.EngageFloorAltitude or 500, self.EngageCeilingAltitude or 1000 )
local EngageSpeed = math.random( self.EngageMinSpeed, self.EngageMaxSpeed )
local DefenderCoord = DefenderGroup:GetPointVec3()
DefenderCoord:SetY( EngageAltitude ) -- Ground targets don't have an altitude.
local TargetCoord = AttackSetUnit:GetRandomSurely():GetPointVec3()
if not TargetCoord then
self:Return()
return
end
TargetCoord:SetY( EngageAltitude ) -- Ground targets don't have an altitude.
local TargetDistance = DefenderCoord:Get2DDistance( TargetCoord )
local EngageDistance = ( DefenderGroup:IsHelicopter() and 5000 ) or ( DefenderGroup:IsAirPlane() and 10000 )
local EngageRoute = {}
local AttackTasks = {}
local FromWP = DefenderCoord:WaypointAir(self.EngageAltType or "RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, EngageSpeed, true)
EngageRoute[#EngageRoute+1] = FromWP
self:SetTargetDistance( TargetCoord ) -- For RTB status check
local FromEngageAngle = DefenderCoord:GetAngleDegrees( DefenderCoord:GetDirectionVec3( TargetCoord ) )
local ToCoord=DefenderCoord:Translate( EngageDistance, FromEngageAngle, true )
local ToWP = ToCoord:WaypointAir(self.EngageAltType or "RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, EngageSpeed, true)
EngageRoute[#EngageRoute+1] = ToWP
-- TODO: A factor of * 3 this way too low. This causes the AI NOT to engage until very close or even merged sometimes. Some A2A missiles have a much longer range! Needs more frequent updates of the task!
if TargetDistance <= EngageDistance * 9 then
local AttackUnitTasks = self:CreateAttackUnitTasks( AttackSetUnit, DefenderGroup, EngageAltitude ) -- Polymorphic
if #AttackUnitTasks == 0 then
self:T( DefenderGroupName .. ": No valid targets found -> Going RTB")
self:Return()
return
else
local text=string.format("%s: Engaging targets at distance %.2f NM", DefenderGroupName, UTILS.MetersToNM(TargetDistance))
self:T(text)
DefenderGroup:OptionROEOpenFire()
DefenderGroup:OptionROTEvadeFire()
DefenderGroup:OptionKeepWeaponsOnThreat()
AttackTasks[#AttackTasks+1] = DefenderGroup:TaskCombo( AttackUnitTasks )
end
end
AttackTasks[#AttackTasks+1] = DefenderGroup:TaskFunction( "AI_AIR_ENGAGE.___Engage", self, AttackSetUnit )
EngageRoute[#EngageRoute].task = DefenderGroup:TaskCombo( AttackTasks )
DefenderGroup:Route( EngageRoute, self.TaskDelay or 0.1 )
end
else
-- TODO: This will make an A2A Dispatcher CAP flight to return rather than going back to patrolling!
self:T( DefenderGroupName .. ": No targets found -> returning.")
self:Return()
return
end
end
-- @param Wrapper.Group#GROUP AIEngage
function AI_AIR_ENGAGE.Resume( AIEngage, Fsm )
AIEngage:F( { "Resume:", AIEngage:GetName() } )
if AIEngage and AIEngage:IsAlive() then
Fsm:__Reset( Fsm.TaskDelay or 0.1 )
Fsm:__EngageRoute( Fsm.TaskDelay or 0.2, Fsm.AttackSetUnit )
end
end

View File

@@ -1,391 +0,0 @@
--- **AI** - Models the process of A2G patrolling and engaging ground targets for airplanes and helicopters.
--
-- ===
--
-- ### Author: **FlightControl**
--
-- ===
--
-- @module AI.AI_Air_Patrol
-- @image AI_Air_To_Ground_Patrol.JPG
-- @type AI_AIR_PATROL
-- @extends AI.AI_Air#AI_AIR
--- The AI_AIR_PATROL class implements the core functions to patrol a @{Core.Zone} by an AI @{Wrapper.Group}
-- and automatically engage any airborne enemies that are within a certain range or within a certain zone.
--
-- ![Process](..\Presentations\AI_CAP\Dia3.JPG)
--
-- The AI_AIR_PATROL is assigned a @{Wrapper.Group} and this must be done before the AI_AIR_PATROL process can be started using the **Start** event.
--
-- ![Process](..\Presentations\AI_CAP\Dia4.JPG)
--
-- The AI will fly towards the random 3D point within the patrol zone, using a random speed within the given altitude and speed limits.
-- Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits.
--
-- ![Process](..\Presentations\AI_CAP\Dia5.JPG)
--
-- This cycle will continue.
--
-- ![Process](..\Presentations\AI_CAP\Dia6.JPG)
--
-- During the patrol, the AI will detect enemy targets, which are reported through the **Detected** event.
--
-- ![Process](..\Presentations\AI_CAP\Dia9.JPG)
--
-- When enemies are detected, the AI will automatically engage the enemy.
--
-- ![Process](..\Presentations\AI_CAP\Dia10.JPG)
--
-- Until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
-- When the fuel threshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
--
-- ![Process](..\Presentations\AI_CAP\Dia13.JPG)
--
-- ## 1. AI_AIR_PATROL constructor
--
-- * @{#AI_AIR_PATROL.New}(): Creates a new AI_AIR_PATROL object.
--
-- ## 2. AI_AIR_PATROL is a FSM
--
-- ![Process](..\Presentations\AI_CAP\Dia2.JPG)
--
-- ### 2.1 AI_AIR_PATROL States
--
-- * **None** ( Group ): The process is not started yet.
-- * **Patrolling** ( Group ): The AI is patrolling the Patrol Zone.
-- * **Engaging** ( Group ): The AI is engaging the bogeys.
-- * **Returning** ( Group ): The AI is returning to Base..
--
-- ### 2.2 AI_AIR_PATROL Events
--
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Start}**: Start the process.
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.PatrolRoute}**: Route the AI to a new random 3D point within the Patrol Zone.
-- * **@{#AI_AIR_PATROL.Engage}**: Let the AI engage the bogeys.
-- * **@{#AI_AIR_PATROL.Abort}**: Aborts the engagement and return patrolling in the patrol zone.
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.RTB}**: Route the AI to the home base.
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detect}**: The AI is detecting targets.
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets.
-- * **@{#AI_AIR_PATROL.Destroy}**: The AI has destroyed a bogey @{Wrapper.Unit}.
-- * **@{#AI_AIR_PATROL.Destroyed}**: The AI has destroyed all bogeys @{Wrapper.Unit}s assigned in the CAS task.
-- * **Status** ( Group ): The AI is checking status (fuel and damage). When the thresholds have been reached, the AI will RTB.
--
-- ## 3. Set the Range of Engagement
--
-- ![Range](..\Presentations\AI_CAP\Dia11.JPG)
--
-- An optional range can be set in meters,
-- that will define when the AI will engage with the detected airborne enemy targets.
-- The range can be beyond or smaller than the range of the Patrol Zone.
-- The range is applied at the position of the AI.
-- Use the method @{#AI_AIR_PATROL.SetEngageRange}() to define that range.
--
-- # Developer Note
--
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
-- Therefore, this class is considered to be deprecated
--
-- ===
--
-- @field #AI_AIR_PATROL
AI_AIR_PATROL = {
ClassName = "AI_AIR_PATROL",
}
--- Creates a new AI_AIR_PATROL object
-- @param #AI_AIR_PATROL self
-- @param AI.AI_Air#AI_AIR AI_Air The AI_AIR FSM.
-- @param Wrapper.Group#GROUP AIGroup The AI group.
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Core.Zone} where the patrol needs to be executed.
-- @param DCS#Altitude PatrolFloorAltitude (optional, default = 1000m ) The lowest altitude in meters where to execute the patrol.
-- @param DCS#Altitude PatrolCeilingAltitude (optional, default = 1500m ) The highest altitude in meters where to execute the patrol.
-- @param DCS#Speed PatrolMinSpeed (optional, default = 50% of max speed) The minimum speed of the @{Wrapper.Group} in km/h.
-- @param DCS#Speed PatrolMaxSpeed (optional, default = 75% of max speed) The maximum speed of the @{Wrapper.Group} in km/h.
-- @param DCS#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO.
-- @return #AI_AIR_PATROL
function AI_AIR_PATROL:New( AI_Air, AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType )
-- Inherits from BASE
local self = BASE:Inherit( self, AI_Air ) -- #AI_AIR_PATROL
local SpeedMax = AIGroup:GetSpeedMax()
self.PatrolZone = PatrolZone
self.PatrolFloorAltitude = PatrolFloorAltitude or 1000
self.PatrolCeilingAltitude = PatrolCeilingAltitude or 1500
self.PatrolMinSpeed = PatrolMinSpeed or SpeedMax * 0.5
self.PatrolMaxSpeed = PatrolMaxSpeed or SpeedMax * 0.75
-- defafult PatrolAltType to "RADIO" if not specified
self.PatrolAltType = PatrolAltType or "RADIO"
self:AddTransition( { "Started", "Airborne", "Refuelling" }, "Patrol", "Patrolling" )
--- OnBefore Transition Handler for Event Patrol.
-- @function [parent=#AI_AIR_PATROL] OnBeforePatrol
-- @param #AI_AIR_PATROL self
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Patrol.
-- @function [parent=#AI_AIR_PATROL] OnAfterPatrol
-- @param #AI_AIR_PATROL self
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Patrol.
-- @function [parent=#AI_AIR_PATROL] Patrol
-- @param #AI_AIR_PATROL self
--- Asynchronous Event Trigger for Event Patrol.
-- @function [parent=#AI_AIR_PATROL] __Patrol
-- @param #AI_AIR_PATROL self
-- @param #number Delay The delay in seconds.
--- OnLeave Transition Handler for State Patrolling.
-- @function [parent=#AI_AIR_PATROL] OnLeavePatrolling
-- @param #AI_AIR_PATROL self
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnEnter Transition Handler for State Patrolling.
-- @function [parent=#AI_AIR_PATROL] OnEnterPatrolling
-- @param #AI_AIR_PATROL self
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
self:AddTransition( "Patrolling", "PatrolRoute", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_AIR_PATROL.
--- OnBefore Transition Handler for Event PatrolRoute.
-- @function [parent=#AI_AIR_PATROL] OnBeforePatrolRoute
-- @param #AI_AIR_PATROL self
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event PatrolRoute.
-- @function [parent=#AI_AIR_PATROL] OnAfterPatrolRoute
-- @param #AI_AIR_PATROL self
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event PatrolRoute.
-- @function [parent=#AI_AIR_PATROL] PatrolRoute
-- @param #AI_AIR_PATROL self
--- Asynchronous Event Trigger for Event PatrolRoute.
-- @function [parent=#AI_AIR_PATROL] __PatrolRoute
-- @param #AI_AIR_PATROL self
-- @param #number Delay The delay in seconds.
self:AddTransition( "*", "Reset", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_AIR_PATROL.
return self
end
--- Set the Engage Range when the AI will engage with airborne enemies.
-- @param #AI_AIR_PATROL self
-- @param #number EngageRange The Engage Range.
-- @return #AI_AIR_PATROL self
function AI_AIR_PATROL:SetEngageRange( EngageRange )
self:F2()
if EngageRange then
self.EngageRange = EngageRange
else
self.EngageRange = nil
end
end
--- Set race track parameters. CAP flights will perform race track patterns rather than randomly patrolling the zone.
-- @param #AI_AIR_PATROL self
-- @param #number LegMin Min Length of the race track leg in meters. Default 10,000 m.
-- @param #number LegMax Max length of the race track leg in meters. Default 15,000 m.
-- @param #number HeadingMin Min heading of the race track in degrees. Default 0 deg, i.e. from South to North.
-- @param #number HeadingMax Max heading of the race track in degrees. Default 180 deg, i.e. from South to North.
-- @param #number DurationMin (Optional) Min duration before switching the orbit position. Default is keep same orbit until RTB or engage.
-- @param #number DurationMax (Optional) Max duration before switching the orbit position. Default is keep same orbit until RTB or engage.
-- @param #table CapCoordinates Table of coordinates of first race track point. Second point is determined by leg length and heading.
-- @return #AI_AIR_PATROL self
function AI_AIR_PATROL:SetRaceTrackPattern(LegMin, LegMax, HeadingMin, HeadingMax, DurationMin, DurationMax, CapCoordinates)
self.racetrack=true
self.racetracklegmin=LegMin or 10000
self.racetracklegmax=LegMax or 15000
self.racetrackheadingmin=HeadingMin or 0
self.racetrackheadingmax=HeadingMax or 180
self.racetrackdurationmin=DurationMin
self.racetrackdurationmax=DurationMax
if self.racetrackdurationmax and not self.racetrackdurationmin then
self.racetrackdurationmin=self.racetrackdurationmax
end
self.racetrackcapcoordinates=CapCoordinates
end
--- Defines a new patrol route using the @{AI.AI_Patrol#AI_PATROL_ZONE} parameters and settings.
-- @param #AI_AIR_PATROL self
-- @return #AI_AIR_PATROL self
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_AIR_PATROL:onafterPatrol( AIPatrol, From, Event, To )
self:F2()
self:ClearTargetDistance()
self:__PatrolRoute( self.TaskDelay )
AIPatrol:OnReSpawn(
function( PatrolGroup )
self:__Reset( self.TaskDelay )
self:__PatrolRoute( self.TaskDelay )
end
)
end
--- This static method is called from the route path within the last task at the last waypoint of the AIPatrol.
-- Note that this method is required, as triggers the next route when patrolling for the AIPatrol.
-- @param Wrapper.Group#GROUP AIPatrol The AI group.
-- @param #AI_AIR_PATROL Fsm The FSM.
function AI_AIR_PATROL.___PatrolRoute( AIPatrol, Fsm )
AIPatrol:F( { "AI_AIR_PATROL.___PatrolRoute:", AIPatrol:GetName() } )
if AIPatrol and AIPatrol:IsAlive() then
Fsm:PatrolRoute()
end
end
--- Defines a new patrol route using the @{AI.AI_Patrol#AI_PATROL_ZONE} parameters and settings.
-- @param #AI_AIR_PATROL self
-- @param Wrapper.Group#GROUP AIPatrol The Group managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_AIR_PATROL:onafterPatrolRoute( AIPatrol, From, Event, To )
self:F2()
-- When RTB, don't allow anymore the routing.
if From == "RTB" then
return
end
if AIPatrol and AIPatrol:IsAlive() then
local PatrolRoute = {}
--- Calculate the target route point.
local CurrentCoord = AIPatrol:GetCoordinate()
local altitude= math.random( self.PatrolFloorAltitude, self.PatrolCeilingAltitude )
local ToTargetCoord = self.PatrolZone:GetRandomPointVec2()
ToTargetCoord:SetAlt( altitude )
self:SetTargetDistance( ToTargetCoord ) -- For RTB status check
local ToTargetSpeed = math.random( self.PatrolMinSpeed, self.PatrolMaxSpeed )
local speedkmh=ToTargetSpeed
local FromWP = CurrentCoord:WaypointAir(self.PatrolAltType or "RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, ToTargetSpeed, true)
PatrolRoute[#PatrolRoute+1] = FromWP
if self.racetrack then
-- Random heading.
local heading = math.random(self.racetrackheadingmin, self.racetrackheadingmax)
-- Random leg length.
local leg=math.random(self.racetracklegmin, self.racetracklegmax)
-- Random duration if any.
local duration = self.racetrackdurationmin
if self.racetrackdurationmax then
duration=math.random(self.racetrackdurationmin, self.racetrackdurationmax)
end
-- CAP coordinate.
local c0=self.PatrolZone:GetRandomCoordinate()
if self.racetrackcapcoordinates and #self.racetrackcapcoordinates>0 then
c0=self.racetrackcapcoordinates[math.random(#self.racetrackcapcoordinates)]
end
-- Race track points.
local c1=c0:SetAltitude(altitude) --Core.Point#COORDINATE
local c2=c1:Translate(leg, heading):SetAltitude(altitude)
self:SetTargetDistance(c0) -- For RTB status check
-- Debug:
self:T(string.format("Patrol zone race track: v=%.1f knots, h=%.1f ft, heading=%03d, leg=%d m, t=%s sec", UTILS.KmphToKnots(speedkmh), UTILS.MetersToFeet(altitude), heading, leg, tostring(duration)))
--c1:MarkToAll("Race track c1")
--c2:MarkToAll("Race track c2")
-- Task to orbit.
local taskOrbit=AIPatrol:TaskOrbit(c1, altitude, UTILS.KmphToMps(speedkmh), c2)
-- Task function to redo the patrol at other random position.
local taskPatrol=AIPatrol:TaskFunction("AI_AIR_PATROL.___PatrolRoute", self)
-- Controlled task with task condition.
local taskCond=AIPatrol:TaskCondition(nil, nil, nil, nil, duration, nil)
local taskCont=AIPatrol:TaskControlled(taskOrbit, taskCond)
-- Second waypoint
PatrolRoute[2]=c1:WaypointAirTurningPoint(self.PatrolAltType, speedkmh, {taskCont, taskPatrol}, "CAP Orbit")
else
--- Create a route point of type air.
local ToWP = ToTargetCoord:WaypointAir(self.PatrolAltType, POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, ToTargetSpeed, true)
PatrolRoute[#PatrolRoute+1] = ToWP
local Tasks = {}
Tasks[#Tasks+1] = AIPatrol:TaskFunction("AI_AIR_PATROL.___PatrolRoute", self)
PatrolRoute[#PatrolRoute].task = AIPatrol:TaskCombo( Tasks )
end
AIPatrol:OptionROEReturnFire()
AIPatrol:OptionROTEvadeFire()
AIPatrol:Route( PatrolRoute, self.TaskDelay )
end
end
--- Resumes the AIPatrol
-- @param Wrapper.Group#GROUP AIPatrol
-- @param Core.Fsm#FSM Fsm
function AI_AIR_PATROL.Resume( AIPatrol, Fsm )
AIPatrol:F( { "AI_AIR_PATROL.Resume:", AIPatrol:GetName() } )
if AIPatrol and AIPatrol:IsAlive() then
Fsm:__Reset( Fsm.TaskDelay )
Fsm:__PatrolRoute( Fsm.TaskDelay )
end
end

View File

@@ -1,294 +0,0 @@
--- **AI** - Models squadrons for airplanes and helicopters.
--
-- This is a class used in the @{AI.AI_Air_Dispatcher} and derived dispatcher classes.
--
-- ===
--
-- ### Author: **FlightControl**
--
-- ===
--
-- @module AI.AI_Air_Squadron
-- @image MOOSE.JPG
-- @type AI_AIR_SQUADRON
-- @extends Core.Base#BASE
--- Implements the core functions modeling squadrons for airplanes and helicopters.
--
-- # Developer Note
--
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
-- Therefore, this class is considered to be deprecated
--
-- ===
--
-- @field #AI_AIR_SQUADRON
AI_AIR_SQUADRON = {
ClassName = "AI_AIR_SQUADRON",
}
--- Creates a new AI_AIR_SQUADRON object
-- @param #AI_AIR_SQUADRON self
-- @return #AI_AIR_SQUADRON
function AI_AIR_SQUADRON:New( SquadronName, AirbaseName, TemplatePrefixes, ResourceCount )
self:T( { Air_Squadron = { SquadronName, AirbaseName, TemplatePrefixes, ResourceCount } } )
local AI_Air_Squadron = BASE:New() -- #AI_AIR_SQUADRON
AI_Air_Squadron.Name = SquadronName
AI_Air_Squadron.Airbase = AIRBASE:FindByName( AirbaseName )
AI_Air_Squadron.AirbaseName = AI_Air_Squadron.Airbase:GetName()
if not AI_Air_Squadron.Airbase then
error( "Cannot find airbase with name:" .. AirbaseName )
end
AI_Air_Squadron.Spawn = {}
if type( TemplatePrefixes ) == "string" then
local SpawnTemplate = TemplatePrefixes
self.DefenderSpawns[SpawnTemplate] = self.DefenderSpawns[SpawnTemplate] or SPAWN:New( SpawnTemplate ) -- :InitCleanUp( 180 )
AI_Air_Squadron.Spawn[1] = self.DefenderSpawns[SpawnTemplate]
else
for TemplateID, SpawnTemplate in pairs( TemplatePrefixes ) do
self.DefenderSpawns[SpawnTemplate] = self.DefenderSpawns[SpawnTemplate] or SPAWN:New( SpawnTemplate ) -- :InitCleanUp( 180 )
AI_Air_Squadron.Spawn[#AI_Air_Squadron.Spawn+1] = self.DefenderSpawns[SpawnTemplate]
end
end
AI_Air_Squadron.ResourceCount = ResourceCount
AI_Air_Squadron.TemplatePrefixes = TemplatePrefixes
AI_Air_Squadron.Captured = false -- Not captured. This flag will be set to true, when the airbase where the squadron is located, is captured.
self:SetSquadronLanguage( SquadronName, "EN" ) -- Squadrons speak English by default.
return AI_Air_Squadron
end
--- Set the Name of the Squadron.
-- @param #AI_AIR_SQUADRON self
-- @param #string Name The Squadron Name.
-- @return #AI_AIR_SQUADRON The Squadron.
function AI_AIR_SQUADRON:SetName( Name )
self.Name = Name
return self
end
--- Get the Name of the Squadron.
-- @param #AI_AIR_SQUADRON self
-- @return #string The Squadron Name.
function AI_AIR_SQUADRON:GetName()
return self.Name
end
--- Set the ResourceCount of the Squadron.
-- @param #AI_AIR_SQUADRON self
-- @param #number ResourceCount The Squadron ResourceCount.
-- @return #AI_AIR_SQUADRON The Squadron.
function AI_AIR_SQUADRON:SetResourceCount( ResourceCount )
self.ResourceCount = ResourceCount
return self
end
--- Get the ResourceCount of the Squadron.
-- @param #AI_AIR_SQUADRON self
-- @return #number The Squadron ResourceCount.
function AI_AIR_SQUADRON:GetResourceCount()
return self.ResourceCount
end
--- Add Resources to the Squadron.
-- @param #AI_AIR_SQUADRON self
-- @param #number Resources The Resources to be added.
-- @return #AI_AIR_SQUADRON The Squadron.
function AI_AIR_SQUADRON:AddResources( Resources )
self.ResourceCount = self.ResourceCount + Resources
return self
end
--- Remove Resources to the Squadron.
-- @param #AI_AIR_SQUADRON self
-- @param #number Resources The Resources to be removed.
-- @return #AI_AIR_SQUADRON The Squadron.
function AI_AIR_SQUADRON:RemoveResources( Resources )
self.ResourceCount = self.ResourceCount - Resources
return self
end
--- Set the Overhead of the Squadron.
-- @param #AI_AIR_SQUADRON self
-- @param #number Overhead The Squadron Overhead.
-- @return #AI_AIR_SQUADRON The Squadron.
function AI_AIR_SQUADRON:SetOverhead( Overhead )
self.Overhead = Overhead
return self
end
--- Get the Overhead of the Squadron.
-- @param #AI_AIR_SQUADRON self
-- @return #number The Squadron Overhead.
function AI_AIR_SQUADRON:GetOverhead()
return self.Overhead
end
--- Set the Grouping of the Squadron.
-- @param #AI_AIR_SQUADRON self
-- @param #number Grouping The Squadron Grouping.
-- @return #AI_AIR_SQUADRON The Squadron.
function AI_AIR_SQUADRON:SetGrouping( Grouping )
self.Grouping = Grouping
return self
end
--- Get the Grouping of the Squadron.
-- @param #AI_AIR_SQUADRON self
-- @return #number The Squadron Grouping.
function AI_AIR_SQUADRON:GetGrouping()
return self.Grouping
end
--- Set the FuelThreshold of the Squadron.
-- @param #AI_AIR_SQUADRON self
-- @param #number FuelThreshold The Squadron FuelThreshold.
-- @return #AI_AIR_SQUADRON The Squadron.
function AI_AIR_SQUADRON:SetFuelThreshold( FuelThreshold )
self.FuelThreshold = FuelThreshold
return self
end
--- Get the FuelThreshold of the Squadron.
-- @param #AI_AIR_SQUADRON self
-- @return #number The Squadron FuelThreshold.
function AI_AIR_SQUADRON:GetFuelThreshold()
return self.FuelThreshold
end
--- Set the EngageProbability of the Squadron.
-- @param #AI_AIR_SQUADRON self
-- @param #number EngageProbability The Squadron EngageProbability.
-- @return #AI_AIR_SQUADRON The Squadron.
function AI_AIR_SQUADRON:SetEngageProbability( EngageProbability )
self.EngageProbability = EngageProbability
return self
end
--- Get the EngageProbability of the Squadron.
-- @param #AI_AIR_SQUADRON self
-- @return #number The Squadron EngageProbability.
function AI_AIR_SQUADRON:GetEngageProbability()
return self.EngageProbability
end
--- Set the Takeoff of the Squadron.
-- @param #AI_AIR_SQUADRON self
-- @param #number Takeoff The Squadron Takeoff.
-- @return #AI_AIR_SQUADRON The Squadron.
function AI_AIR_SQUADRON:SetTakeoff( Takeoff )
self.Takeoff = Takeoff
return self
end
--- Get the Takeoff of the Squadron.
-- @param #AI_AIR_SQUADRON self
-- @return #number The Squadron Takeoff.
function AI_AIR_SQUADRON:GetTakeoff()
return self.Takeoff
end
--- Set the Landing of the Squadron.
-- @param #AI_AIR_SQUADRON self
-- @param #number Landing The Squadron Landing.
-- @return #AI_AIR_SQUADRON The Squadron.
function AI_AIR_SQUADRON:SetLanding( Landing )
self.Landing = Landing
return self
end
--- Get the Landing of the Squadron.
-- @param #AI_AIR_SQUADRON self
-- @return #number The Squadron Landing.
function AI_AIR_SQUADRON:GetLanding()
return self.Landing
end
--- Set the TankerName of the Squadron.
-- @param #AI_AIR_SQUADRON self
-- @param #string TankerName The Squadron Tanker Name.
-- @return #AI_AIR_SQUADRON The Squadron.
function AI_AIR_SQUADRON:SetTankerName( TankerName )
self.TankerName = TankerName
return self
end
--- Get the Tanker Name of the Squadron.
-- @param #AI_AIR_SQUADRON self
-- @return #string The Squadron Tanker Name.
function AI_AIR_SQUADRON:GetTankerName()
return self.TankerName
end
--- Set the Radio of the Squadron.
-- @param #AI_AIR_SQUADRON self
-- @param #number RadioFrequency The frequency of communication.
-- @param #number RadioModulation The modulation of communication.
-- @param #number RadioPower The power in Watts of communication.
-- @param #string Language The language of the radio speech.
-- @return #AI_AIR_SQUADRON The Squadron.
function AI_AIR_SQUADRON:SetRadio( RadioFrequency, RadioModulation, RadioPower, Language )
self.RadioFrequency = RadioFrequency
self.RadioModulation = RadioModulation or radio.modulation.AM
self.RadioPower = RadioPower or 100
if self.RadioSpeech then
self.RadioSpeech:Stop()
end
self.RadioSpeech = nil
self.RadioSpeech = RADIOSPEECH:New( RadioFrequency, RadioModulation )
self.RadioSpeech.power = RadioPower
self.RadioSpeech:Start( 0.5 )
self.RadioSpeech:SetLanguage( Language )
return self
end

View File

@@ -1,652 +0,0 @@
--- **AI** - Peform Battlefield Area Interdiction (BAI) within an engagement zone.
--
-- **Features:**
--
-- * Hold and standby within a patrol zone.
-- * Engage upon command the assigned targets within an engagement zone.
-- * Loop the zone until all targets are eliminated.
-- * Trigger different events upon the results achieved.
-- * After combat, return to the patrol zone and hold.
-- * RTB when commanded or after out of fuel.
--
-- ===
--
-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AI/AI_BAI)
--
-- ===
--
-- ### [YouTube Playlist]()
--
-- ===
--
-- ### Author: **FlightControl**
-- ### Contributions:
--
-- * **Gunterlund**: Test case revision.
--
-- ===
--
-- @module AI.AI_BAI
-- @image AI_Battlefield_Air_Interdiction.JPG
--- AI_BAI_ZONE class
-- @type AI_BAI_ZONE
-- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Wrapper.Controllable} patrolling.
-- @field Core.Zone#ZONE_BASE TargetZone The @{Core.Zone} where the patrol needs to be executed.
-- @extends AI.AI_Patrol#AI_PATROL_ZONE
--- Implements the core functions to provide BattleGround Air Interdiction in an Engage @{Core.Zone} by an AIR @{Wrapper.Controllable} or @{Wrapper.Group}.
--
-- The AI_BAI_ZONE runs a process. It holds an AI in a Patrol Zone and when the AI is commanded to engage, it will fly to an Engage Zone.
--
-- ![HoldAndEngage](..\Presentations\AI_BAI\Dia3.JPG)
--
-- The AI_BAI_ZONE is assigned a @{Wrapper.Group} and this must be done before the AI_BAI_ZONE process can be started through the **Start** event.
--
-- ![Start Event](..\Presentations\AI_BAI\Dia4.JPG)
--
-- Upon started, The AI will **Route** itself towards the random 3D point within a patrol zone,
-- using a random speed within the given altitude and speed limits.
-- Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits.
-- This cycle will continue until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
--
-- ![Route Event](..\Presentations\AI_BAI\Dia5.JPG)
--
-- When the AI is commanded to provide BattleGround Air Interdiction (through the event **Engage**), the AI will fly towards the Engage Zone.
-- Any target that is detected in the Engage Zone will be reported and will be destroyed by the AI.
--
-- ![Engage Event](..\Presentations\AI_BAI\Dia6.JPG)
--
-- The AI will detect the targets and will only destroy the targets within the Engage Zone.
--
-- ![Engage Event](..\Presentations\AI_BAI\Dia7.JPG)
--
-- Every target that is destroyed, is reported< by the AI.
--
-- ![Engage Event](..\Presentations\AI_BAI\Dia8.JPG)
--
-- Note that the AI does not know when the Engage Zone is cleared, and therefore will keep circling in the zone.
--
-- ![Engage Event](..\Presentations\AI_BAI\Dia9.JPG)
--
-- Until it is notified through the event **Accomplish**, which is to be triggered by an observing party:
--
-- * a FAC
-- * a timed event
-- * a menu option selected by a human
-- * a condition
-- * others ...
--
-- ![Engage Event](..\Presentations\AI_BAI\Dia10.JPG)
--
-- When the AI has accomplished the Bombing, it will fly back to the Patrol Zone.
--
-- ![Engage Event](..\Presentations\AI_BAI\Dia11.JPG)
--
-- It will keep patrolling there, until it is notified to RTB or move to another BOMB Zone.
-- It can be notified to go RTB through the **RTB** event.
--
-- When the fuel threshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
--
-- ![Engage Event](..\Presentations\AI_BAI\Dia12.JPG)
--
-- # 1. AI_BAI_ZONE constructor
--
-- * @{#AI_BAI_ZONE.New}(): Creates a new AI_BAI_ZONE object.
--
-- ## 2. AI_BAI_ZONE is a FSM
--
-- ![Process](..\Presentations\AI_BAI\Dia2.JPG)
--
-- ### 2.1. AI_BAI_ZONE States
--
-- * **None** ( Group ): The process is not started yet.
-- * **Patrolling** ( Group ): The AI is patrolling the Patrol Zone.
-- * **Engaging** ( Group ): The AI is engaging the targets in the Engage Zone, executing BOMB.
-- * **Returning** ( Group ): The AI is returning to Base..
--
-- ### 2.2. AI_BAI_ZONE Events
--
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Start}**: Start the process.
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Route}**: Route the AI to a new random 3D point within the Patrol Zone.
-- * **@{#AI_BAI_ZONE.Engage}**: Engage the AI to provide BOMB in the Engage Zone, destroying any target it finds.
-- * **@{#AI_BAI_ZONE.Abort}**: Aborts the engagement and return patrolling in the patrol zone.
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.RTB}**: Route the AI to the home base.
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detect}**: The AI is detecting targets.
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets.
-- * **@{#AI_BAI_ZONE.Destroy}**: The AI has destroyed a target @{Wrapper.Unit}.
-- * **@{#AI_BAI_ZONE.Destroyed}**: The AI has destroyed all target @{Wrapper.Unit}s assigned in the BOMB task.
-- * **Status**: The AI is checking status (fuel and damage). When the thresholds have been reached, the AI will RTB.
--
-- ## 3. Modify the Engage Zone behaviour to pinpoint a **map object** or **scenery object**
--
-- Use the method @{#AI_BAI_ZONE.SearchOff}() to specify that the EngageZone is not to be searched for potential targets (UNITs), but that the center of the zone
-- is the point where a map object is to be destroyed (like a bridge).
--
-- Example:
--
-- -- Tell the BAI not to search for potential targets in the BAIEngagementZone, but rather use the center of the BAIEngagementZone as the bombing location.
-- AIBAIZone:SearchOff()
--
-- Searching can be switched back on with the method @{#AI_BAI_ZONE.SearchOn}(). Use the method @{#AI_BAI_ZONE.SearchOnOff}() to flexibily switch searching on or off.
--
-- # Developer Note
--
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
-- Therefore, this class is considered to be deprecated
--
-- ===
--
-- @field #AI_BAI_ZONE
AI_BAI_ZONE = {
ClassName = "AI_BAI_ZONE",
}
--- Creates a new AI_BAI_ZONE object
-- @param #AI_BAI_ZONE self
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Core.Zone} where the patrol needs to be executed.
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Controllable} in km/h.
-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Controllable} in km/h.
-- @param Core.Zone#ZONE_BASE EngageZone The zone where the engage will happen.
-- @param DCS#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO
-- @return #AI_BAI_ZONE self
function AI_BAI_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, EngageZone, PatrolAltType )
-- Inherits from BASE
local self = BASE:Inherit( self, AI_PATROL_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType ) ) -- #AI_BAI_ZONE
self.EngageZone = EngageZone
self.Accomplished = false
self:SetDetectionZone( self.EngageZone )
self:SearchOn()
self:AddTransition( { "Patrolling", "Engaging" }, "Engage", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_BAI_ZONE.
--- OnBefore Transition Handler for Event Engage.
-- @function [parent=#AI_BAI_ZONE] OnBeforeEngage
-- @param #AI_BAI_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Engage.
-- @function [parent=#AI_BAI_ZONE] OnAfterEngage
-- @param #AI_BAI_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Engage.
-- @function [parent=#AI_BAI_ZONE] Engage
-- @param #AI_BAI_ZONE self
-- @param #number EngageSpeed (optional) The speed the Group will hold when engaging to the target zone.
-- @param DCS#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement.
-- @param DCS#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack.
-- If parameter is not defined the unit / controllable will choose expend on its own discretion.
-- Use the structure @{DCS#AI.Task.WeaponExpend} to define the amount of weapons to be release at each attack.
-- @param #number EngageAttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo.
-- @param DCS#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction.
--- Asynchronous Event Trigger for Event Engage.
-- @function [parent=#AI_BAI_ZONE] __Engage
-- @param #AI_BAI_ZONE self
-- @param #number Delay The delay in seconds.
-- @param #number EngageSpeed (optional) The speed the Group will hold when engaging to the target zone.
-- @param DCS#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement.
-- @param DCS#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack.
-- If parameter is not defined the unit / controllable will choose expend on its own discretion.
-- Use the structure @{DCS#AI.Task.WeaponExpend} to define the amount of weapons to be release at each attack.
-- @param #number EngageAttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo.
-- @param DCS#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction.
--- OnLeave Transition Handler for State Engaging.
-- @function [parent=#AI_BAI_ZONE] OnLeaveEngaging
-- @param #AI_BAI_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnEnter Transition Handler for State Engaging.
-- @function [parent=#AI_BAI_ZONE] OnEnterEngaging
-- @param #AI_BAI_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
self:AddTransition( "Engaging", "Target", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_BAI_ZONE.
self:AddTransition( "Engaging", "Fired", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_BAI_ZONE.
--- OnBefore Transition Handler for Event Fired.
-- @function [parent=#AI_BAI_ZONE] OnBeforeFired
-- @param #AI_BAI_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Fired.
-- @function [parent=#AI_BAI_ZONE] OnAfterFired
-- @param #AI_BAI_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Fired.
-- @function [parent=#AI_BAI_ZONE] Fired
-- @param #AI_BAI_ZONE self
--- Asynchronous Event Trigger for Event Fired.
-- @function [parent=#AI_BAI_ZONE] __Fired
-- @param #AI_BAI_ZONE self
-- @param #number Delay The delay in seconds.
self:AddTransition( "*", "Destroy", "*" ) -- FSM_CONTROLLABLE Transition for type #AI_BAI_ZONE.
--- OnBefore Transition Handler for Event Destroy.
-- @function [parent=#AI_BAI_ZONE] OnBeforeDestroy
-- @param #AI_BAI_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Destroy.
-- @function [parent=#AI_BAI_ZONE] OnAfterDestroy
-- @param #AI_BAI_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Destroy.
-- @function [parent=#AI_BAI_ZONE] Destroy
-- @param #AI_BAI_ZONE self
--- Asynchronous Event Trigger for Event Destroy.
-- @function [parent=#AI_BAI_ZONE] __Destroy
-- @param #AI_BAI_ZONE self
-- @param #number Delay The delay in seconds.
self:AddTransition( "Engaging", "Abort", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_BAI_ZONE.
--- OnBefore Transition Handler for Event Abort.
-- @function [parent=#AI_BAI_ZONE] OnBeforeAbort
-- @param #AI_BAI_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Abort.
-- @function [parent=#AI_BAI_ZONE] OnAfterAbort
-- @param #AI_BAI_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Abort.
-- @function [parent=#AI_BAI_ZONE] Abort
-- @param #AI_BAI_ZONE self
--- Asynchronous Event Trigger for Event Abort.
-- @function [parent=#AI_BAI_ZONE] __Abort
-- @param #AI_BAI_ZONE self
-- @param #number Delay The delay in seconds.
self:AddTransition( "Engaging", "Accomplish", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_BAI_ZONE.
--- OnBefore Transition Handler for Event Accomplish.
-- @function [parent=#AI_BAI_ZONE] OnBeforeAccomplish
-- @param #AI_BAI_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Accomplish.
-- @function [parent=#AI_BAI_ZONE] OnAfterAccomplish
-- @param #AI_BAI_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Accomplish.
-- @function [parent=#AI_BAI_ZONE] Accomplish
-- @param #AI_BAI_ZONE self
--- Asynchronous Event Trigger for Event Accomplish.
-- @function [parent=#AI_BAI_ZONE] __Accomplish
-- @param #AI_BAI_ZONE self
-- @param #number Delay The delay in seconds.
return self
end
--- Set the Engage Zone where the AI is performing BOMB. Note that if the EngageZone is changed, the AI needs to re-detect targets.
-- @param #AI_BAI_ZONE self
-- @param Core.Zone#ZONE EngageZone The zone where the AI is performing BOMB.
-- @return #AI_BAI_ZONE self
function AI_BAI_ZONE:SetEngageZone( EngageZone )
self:F2()
if EngageZone then
self.EngageZone = EngageZone
else
self.EngageZone = nil
end
end
--- Specifies whether to search for potential targets in the zone, or let the center of the zone be the bombing coordinate.
-- AI_BAI_ZONE will search for potential targets by default.
-- @param #AI_BAI_ZONE self
-- @return #AI_BAI_ZONE
function AI_BAI_ZONE:SearchOnOff( Search )
self.Search = Search
return self
end
--- If Search is Off, the current zone coordinate will be the center of the bombing.
-- @param #AI_BAI_ZONE self
-- @return #AI_BAI_ZONE
function AI_BAI_ZONE:SearchOff()
self:SearchOnOff( false )
return self
end
--- If Search is On, BAI will search for potential targets in the zone.
-- @param #AI_BAI_ZONE self
-- @return #AI_BAI_ZONE
function AI_BAI_ZONE:SearchOn()
self:SearchOnOff( true )
return self
end
--- onafter State Transition for Event Start.
-- @param #AI_BAI_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_BAI_ZONE:onafterStart( Controllable, From, Event, To )
-- Call the parent Start event handler
self:GetParent(self).onafterStart( self, Controllable, From, Event, To )
self:HandleEvent( EVENTS.Dead )
self:SetDetectionDeactivated() -- When not engaging, set the detection off.
end
-- @param Wrapper.Controllable#CONTROLLABLE AIControllable
function _NewEngageRoute( AIControllable )
AIControllable:T( "NewEngageRoute" )
local EngageZone = AIControllable:GetState( AIControllable, "EngageZone" ) -- AI.AI_BAI#AI_BAI_ZONE
EngageZone:__Engage( 1, EngageZone.EngageSpeed, EngageZone.EngageAltitude, EngageZone.EngageWeaponExpend, EngageZone.EngageAttackQty, EngageZone.EngageDirection )
end
-- @param #AI_BAI_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_BAI_ZONE:onbeforeEngage( Controllable, From, Event, To )
if self.Accomplished == true then
return false
end
end
-- @param #AI_BAI_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_BAI_ZONE:onafterTarget( Controllable, From, Event, To )
self:F({"onafterTarget",self.Search,Controllable:IsAlive()})
if Controllable:IsAlive() then
local AttackTasks = {}
if self.Search == true then
for DetectedUnit, Detected in pairs( self.DetectedUnits ) do
local DetectedUnit = DetectedUnit -- Wrapper.Unit#UNIT
if DetectedUnit:IsAlive() then
if DetectedUnit:IsInZone( self.EngageZone ) then
if Detected == true then
self:F( {"Target: ", DetectedUnit } )
self.DetectedUnits[DetectedUnit] = false
local AttackTask = Controllable:TaskAttackUnit( DetectedUnit, false, self.EngageWeaponExpend, self.EngageAttackQty, self.EngageDirection, self.EngageAltitude, nil )
self.Controllable:PushTask( AttackTask, 1 )
end
end
else
self.DetectedUnits[DetectedUnit] = nil
end
end
else
self:F("Attack zone")
local AttackTask = Controllable:TaskAttackMapObject(
self.EngageZone:GetPointVec2():GetVec2(),
true,
self.EngageWeaponExpend,
self.EngageAttackQty,
self.EngageDirection,
self.EngageAltitude
)
self.Controllable:PushTask( AttackTask, 1 )
end
self:__Target( -10 )
end
end
-- @param #AI_BAI_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_BAI_ZONE:onafterAbort( Controllable, From, Event, To )
Controllable:ClearTasks()
self:__Route( 1 )
end
-- @param #AI_BAI_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @param #number EngageSpeed (optional) The speed the Group will hold when engaging to the target zone.
-- @param DCS#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement.
-- @param DCS#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion.
-- @param #number EngageAttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo.
-- @param DCS#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction.
function AI_BAI_ZONE:onafterEngage( Controllable, From, Event, To,
EngageSpeed,
EngageAltitude,
EngageWeaponExpend,
EngageAttackQty,
EngageDirection )
self:F("onafterEngage")
self.EngageSpeed = EngageSpeed or 400
self.EngageAltitude = EngageAltitude or 2000
self.EngageWeaponExpend = EngageWeaponExpend
self.EngageAttackQty = EngageAttackQty
self.EngageDirection = EngageDirection
if Controllable:IsAlive() then
local EngageRoute = {}
--- Calculate the current route point.
local CurrentVec2 = self.Controllable:GetVec2()
--DONE: Create GetAltitude function for GROUP, and delete GetUnit(1).
local CurrentAltitude = self.Controllable:GetAltitude()
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
local ToEngageZoneSpeed = self.PatrolMaxSpeed
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
self.PatrolAltType,
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
self.EngageSpeed,
true
)
EngageRoute[#EngageRoute+1] = CurrentRoutePoint
local AttackTasks = {}
if self.Search == true then
for DetectedUnitID, DetectedUnitData in pairs( self.DetectedUnits ) do
local DetectedUnit = DetectedUnitData -- Wrapper.Unit#UNIT
self:T( DetectedUnit )
if DetectedUnit:IsAlive() then
if DetectedUnit:IsInZone( self.EngageZone ) then
self:F( {"Engaging ", DetectedUnit } )
AttackTasks[#AttackTasks+1] = Controllable:TaskBombing(
DetectedUnit:GetPointVec2():GetVec2(),
true,
EngageWeaponExpend,
EngageAttackQty,
EngageDirection,
EngageAltitude
)
end
else
self.DetectedUnits[DetectedUnit] = nil
end
end
else
self:F("Attack zone")
AttackTasks[#AttackTasks+1] = Controllable:TaskAttackMapObject(
self.EngageZone:GetPointVec2():GetVec2(),
true,
EngageWeaponExpend,
EngageAttackQty,
EngageDirection,
EngageAltitude
)
end
EngageRoute[#EngageRoute].task = Controllable:TaskCombo( AttackTasks )
--- Define a random point in the @{Core.Zone}. The AI will fly to that point within the zone.
--- Find a random 2D point in EngageZone.
local ToTargetVec2 = self.EngageZone:GetRandomVec2()
self:T2( ToTargetVec2 )
--- Obtain a 3D @{Point} from the 2D point + altitude.
local ToTargetPointVec3 = POINT_VEC3:New( ToTargetVec2.x, self.EngageAltitude, ToTargetVec2.y )
--- Create a route point of type air.
local ToTargetRoutePoint = ToTargetPointVec3:WaypointAir(
self.PatrolAltType,
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
self.EngageSpeed,
true
)
EngageRoute[#EngageRoute+1] = ToTargetRoutePoint
Controllable:OptionROEOpenFire()
Controllable:OptionROTVertical()
--- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable...
Controllable:WayPointInitialize( EngageRoute )
--- Do a trick, link the NewEngageRoute function of the object to the AIControllable in a temporary variable ...
Controllable:SetState( Controllable, "EngageZone", self )
Controllable:WayPointFunction( #EngageRoute, 1, "_NewEngageRoute" )
--- NOW ROUTE THE GROUP!
Controllable:WayPointExecute( 1 )
self:SetRefreshTimeInterval( 2 )
self:SetDetectionActivated()
self:__Target( -2 ) -- Start targeting
end
end
-- @param #AI_BAI_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_BAI_ZONE:onafterAccomplish( Controllable, From, Event, To )
self.Accomplished = true
self:SetDetectionDeactivated()
end
-- @param #AI_BAI_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @param Core.Event#EVENTDATA EventData
function AI_BAI_ZONE:onafterDestroy( Controllable, From, Event, To, EventData )
if EventData.IniUnit then
self.DetectedUnits[EventData.IniUnit] = nil
end
end
-- @param #AI_BAI_ZONE self
-- @param Core.Event#EVENTDATA EventData
function AI_BAI_ZONE:OnEventDead( EventData )
self:F( { "EventDead", EventData } )
if EventData.IniDCSUnit then
if self.DetectedUnits and self.DetectedUnits[EventData.IniUnit] then
self:__Destroy( 1, EventData )
end
end
end

View File

@@ -1,314 +0,0 @@
--- **AI** - Balance player slots with AI to create an engaging simulation environment, independent of the amount of players.
--
-- **Features:**
--
-- * Automatically spawn AI as a replacement of free player slots for a coalition.
-- * Make the AI to perform tasks.
-- * Define a maximum amount of AI to be active at the same time.
-- * Configure the behaviour of AI when a human joins a slot for which an AI is active.
--
-- ===
--
-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AI/AI_Balancer)
--
-- ===
--
-- ### [YouTube Playlist](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl2CJVIrL1TdAumuVS8n64B7)
--
-- ===
--
-- ### Author: **FlightControl**
-- ### Contributions:
--
-- * **Dutch_Baron**: Working together with James has resulted in the creation of the AI_BALANCER class. James has shared his ideas on balancing AI with air units, and together we made a first design which you can use now :-)
--
-- ===
--
-- @module AI.AI_Balancer
-- @image AI_Balancing.JPG
-- @type AI_BALANCER
-- @field Core.Set#SET_CLIENT SetClient
-- @field Core.Spawn#SPAWN SpawnAI
-- @field Wrapper.Group#GROUP Test
-- @extends Core.Fsm#FSM_SET
--- Monitors and manages as many replacement AI groups as there are
-- CLIENTS in a SET\_CLIENT collection, which are not occupied by human players.
-- In other words, use AI_BALANCER to simulate human behaviour by spawning in replacement AI in multi player missions.
--
-- The parent class @{Core.Fsm#FSM_SET} manages the functionality to control the Finite State Machine (FSM).
-- The mission designer can tailor the behaviour of the AI_BALANCER, by defining event and state transition methods.
-- An explanation about state and event transition methods can be found in the @{Core.Fsm} module documentation.
--
-- The mission designer can tailor the AI_BALANCER behaviour, by implementing a state or event handling method for the following:
--
-- * @{#AI_BALANCER.OnAfterSpawned}( AISet, From, Event, To, AIGroup ): Define to add extra logic when an AI is spawned.
--
-- ## 1. AI_BALANCER construction
--
-- Create a new AI_BALANCER object with the @{#AI_BALANCER.New}() method:
--
-- ## 2. AI_BALANCER is a FSM
--
-- ![Process](..\Presentations\AI_BALANCER\Dia13.JPG)
--
-- ### 2.1. AI_BALANCER States
--
-- * **Monitoring** ( Set ): Monitoring the Set if all AI is spawned for the Clients.
-- * **Spawning** ( Set, ClientName ): There is a new AI group spawned with ClientName as the name of reference.
-- * **Spawned** ( Set, AIGroup ): A new AI has been spawned. You can handle this event to customize the AI behaviour with other AI FSMs or own processes.
-- * **Destroying** ( Set, AIGroup ): The AI is being destroyed.
-- * **Returning** ( Set, AIGroup ): The AI is returning to the airbase specified by the ReturnToAirbase methods. Handle this state to customize the return behaviour of the AI, if any.
--
-- ### 2.2. AI_BALANCER Events
--
-- * **Monitor** ( Set ): Every 10 seconds, the Monitor event is triggered to monitor the Set.
-- * **Spawn** ( Set, ClientName ): Triggers when there is a new AI group to be spawned with ClientName as the name of reference.
-- * **Spawned** ( Set, AIGroup ): Triggers when a new AI has been spawned. You can handle this event to customize the AI behaviour with other AI FSMs or own processes.
-- * **Destroy** ( Set, AIGroup ): The AI is being destroyed.
-- * **Return** ( Set, AIGroup ): The AI is returning to the airbase specified by the ReturnToAirbase methods.
--
-- ## 3. AI_BALANCER spawn interval for replacement AI
--
-- Use the method @{#AI_BALANCER.InitSpawnInterval}() to set the earliest and latest interval in seconds that is waited until a new replacement AI is spawned.
--
-- ## 4. AI_BALANCER returns AI to Airbases
--
-- By default, When a human player joins a slot that is AI_BALANCED, the AI group will be destroyed by default.
-- However, there are 2 additional options that you can use to customize the destroy behaviour.
-- When a human player joins a slot, you can configure to let the AI return to:
--
-- * @{#AI_BALANCER.ReturnToHomeAirbase}: Returns the AI to the **home** @{Wrapper.Airbase#AIRBASE}.
-- * @{#AI_BALANCER.ReturnToNearestAirbases}: Returns the AI to the **nearest friendly** @{Wrapper.Airbase#AIRBASE}.
--
-- Note that when AI returns to an airbase, the AI_BALANCER will trigger the **Return** event and the AI will return,
-- otherwise the AI_BALANCER will trigger a **Destroy** event, and the AI will be destroyed.
--
-- # Developer Note
--
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
-- Therefore, this class is considered to be deprecated
--
-- @field #AI_BALANCER
AI_BALANCER = {
ClassName = "AI_BALANCER",
PatrolZones = {},
AIGroups = {},
Earliest = 5, -- Earliest a new AI can be spawned is in 5 seconds.
Latest = 60, -- Latest a new AI can be spawned is in 60 seconds.
}
--- Creates a new AI_BALANCER object
-- @param #AI_BALANCER self
-- @param Core.Set#SET_CLIENT SetClient A SET\_CLIENT object that will contain the CLIENT objects to be monitored if they are alive or not (joined by a player).
-- @param Core.Spawn#SPAWN SpawnAI The default Spawn object to spawn new AI Groups when needed.
-- @return #AI_BALANCER
function AI_BALANCER:New( SetClient, SpawnAI )
-- Inherits from BASE
local self = BASE:Inherit( self, FSM_SET:New( SET_GROUP:New() ) ) -- AI.AI_Balancer#AI_BALANCER
-- TODO: Define the OnAfterSpawned event
self:SetStartState( "None" )
self:AddTransition( "*", "Monitor", "Monitoring" )
self:AddTransition( "*", "Spawn", "Spawning" )
self:AddTransition( "Spawning", "Spawned", "Spawned" )
self:AddTransition( "*", "Destroy", "Destroying" )
self:AddTransition( "*", "Return", "Returning" )
self.SetClient = SetClient
self.SetClient:FilterOnce()
self.SpawnAI = SpawnAI
self.SpawnQueue = {}
self.ToNearestAirbase = false
self.ToHomeAirbase = false
self:__Monitor( 1 )
return self
end
--- Sets the earliest to the latest interval in seconds how long AI_BALANCER will wait to spawn a new AI.
-- Provide 2 identical seconds if the interval should be a fixed amount of seconds.
-- @param #AI_BALANCER self
-- @param #number Earliest The earliest a new AI can be spawned in seconds.
-- @param #number Latest The latest a new AI can be spawned in seconds.
-- @return self
function AI_BALANCER:InitSpawnInterval( Earliest, Latest )
self.Earliest = Earliest
self.Latest = Latest
return self
end
--- Returns the AI to the nearest friendly @{Wrapper.Airbase#AIRBASE}.
-- @param #AI_BALANCER self
-- @param DCS#Distance ReturnThresholdRange If there is an enemy @{Wrapper.Client#CLIENT} within the ReturnThresholdRange given in meters, the AI will not return to the nearest @{Wrapper.Airbase#AIRBASE}.
-- @param Core.Set#SET_AIRBASE ReturnAirbaseSet The SET of @{Core.Set#SET_AIRBASE}s to evaluate where to return to.
function AI_BALANCER:ReturnToNearestAirbases( ReturnThresholdRange, ReturnAirbaseSet )
self.ToNearestAirbase = true
self.ReturnThresholdRange = ReturnThresholdRange
self.ReturnAirbaseSet = ReturnAirbaseSet
end
--- Returns the AI to the home @{Wrapper.Airbase#AIRBASE}.
-- @param #AI_BALANCER self
-- @param DCS#Distance ReturnThresholdRange If there is an enemy @{Wrapper.Client#CLIENT} within the ReturnThresholdRange given in meters, the AI will not return to the nearest @{Wrapper.Airbase#AIRBASE}.
function AI_BALANCER:ReturnToHomeAirbase( ReturnThresholdRange )
self.ToHomeAirbase = true
self.ReturnThresholdRange = ReturnThresholdRange
end
--- AI_BALANCER:onenterSpawning
-- @param #AI_BALANCER self
-- @param Core.Set#SET_GROUP SetGroup
-- @param #string ClientName
-- @param Wrapper.Group#GROUP AIGroup
function AI_BALANCER:onenterSpawning( SetGroup, From, Event, To, ClientName )
-- OK, Spawn a new group from the default SpawnAI object provided.
local AIGroup = self.SpawnAI:Spawn() -- Wrapper.Group#GROUP
if AIGroup then
AIGroup:T( { "Spawning new AIGroup", ClientName = ClientName } )
--TODO: need to rework UnitName thing ...
SetGroup:Remove( ClientName ) -- Ensure that the previously allocated AIGroup to ClientName is removed in the Set.
SetGroup:Add( ClientName, AIGroup )
self.SpawnQueue[ClientName] = nil
-- Fire the Spawned event. The first parameter is the AIGroup just Spawned.
-- Mission designers can catch this event to bind further actions to the AIGroup.
self:Spawned( AIGroup )
end
end
--- AI_BALANCER:onenterDestroying
-- @param #AI_BALANCER self
-- @param Core.Set#SET_GROUP SetGroup
-- @param Wrapper.Group#GROUP AIGroup
function AI_BALANCER:onenterDestroying( SetGroup, From, Event, To, ClientName, AIGroup )
AIGroup:Destroy()
SetGroup:Flush( self )
SetGroup:Remove( ClientName )
SetGroup:Flush( self )
end
--- RTB
-- @param #AI_BALANCER self
-- @param Core.Set#SET_GROUP SetGroup
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param Wrapper.Group#GROUP AIGroup
function AI_BALANCER:onenterReturning( SetGroup, From, Event, To, AIGroup )
local AIGroupTemplate = AIGroup:GetTemplate()
if self.ToHomeAirbase == true then
local WayPointCount = #AIGroupTemplate.route.points
local SwitchWayPointCommand = AIGroup:CommandSwitchWayPoint( 1, WayPointCount, 1 )
AIGroup:SetCommand( SwitchWayPointCommand )
AIGroup:MessageToRed( "Returning to home base ...", 30 )
else
-- Okay, we need to send this Group back to the nearest base of the Coalition of the AI.
--TODO: i need to rework the POINT_VEC2 thing.
local PointVec2 = POINT_VEC2:New( AIGroup:GetVec2().x, AIGroup:GetVec2().y )
local ClosestAirbase = self.ReturnAirbaseSet:FindNearestAirbaseFromPointVec2( PointVec2 )
self:T( ClosestAirbase.AirbaseName )
--[[
AIGroup:MessageToRed( "Returning to " .. ClosestAirbase:GetName().. " ...", 30 )
local RTBRoute = AIGroup:RouteReturnToAirbase( ClosestAirbase )
AIGroupTemplate.route = RTBRoute
AIGroup:Respawn( AIGroupTemplate )
]]
AIGroup:RouteRTB(ClosestAirbase)
end
end
--- AI_BALANCER:onenterMonitoring
-- @param #AI_BALANCER self
function AI_BALANCER:onenterMonitoring( SetGroup )
self:T2( { self.SetClient:Count() } )
--self.SetClient:Flush()
self.SetClient:ForEachClient(
--- SetClient:ForEachClient
-- @param Wrapper.Client#CLIENT Client
function( Client )
self:T3(Client.ClientName)
local AIGroup = self.Set:Get( Client.UnitName ) -- Wrapper.Group#GROUP
if AIGroup then self:T( { AIGroup = AIGroup:GetName(), IsAlive = AIGroup:IsAlive() } ) end
if Client:IsAlive() == true then
if AIGroup and AIGroup:IsAlive() == true then
if self.ToNearestAirbase == false and self.ToHomeAirbase == false then
self:Destroy( Client.UnitName, AIGroup )
else
-- We test if there is no other CLIENT within the self.ReturnThresholdRange of the first unit of the AI group.
-- If there is a CLIENT, the AI stays engaged and will not return.
-- If there is no CLIENT within the self.ReturnThresholdRange, then the unit will return to the Airbase return method selected.
local PlayerInRange = { Value = false }
local RangeZone = ZONE_RADIUS:New( 'RangeZone', AIGroup:GetVec2(), self.ReturnThresholdRange )
self:T2( RangeZone )
_DATABASE:ForEachPlayerUnit(
--- Nameless function
-- @param Wrapper.Unit#UNIT RangeTestUnit
function( RangeTestUnit, RangeZone, AIGroup, PlayerInRange )
self:T2( { PlayerInRange, RangeTestUnit.UnitName, RangeZone.ZoneName } )
if RangeTestUnit:IsInZone( RangeZone ) == true then
self:T2( "in zone" )
if RangeTestUnit:GetCoalition() ~= AIGroup:GetCoalition() then
self:T2( "in range" )
PlayerInRange.Value = true
end
end
end,
--- Nameless function
-- @param Core.Zone#ZONE_RADIUS RangeZone
-- @param Wrapper.Group#GROUP AIGroup
function( RangeZone, AIGroup, PlayerInRange )
if PlayerInRange.Value == false then
self:Return( AIGroup )
end
end
, RangeZone, AIGroup, PlayerInRange
)
end
self.Set:Remove( Client.UnitName )
end
else
if not AIGroup or not AIGroup:IsAlive() == true then
self:T( "Client " .. Client.UnitName .. " not alive." )
self:T( { Queue = self.SpawnQueue[Client.UnitName] } )
if not self.SpawnQueue[Client.UnitName] then
-- Spawn a new AI taking into account the spawn interval Earliest, Latest
self:__Spawn( math.random( self.Earliest, self.Latest ), Client.UnitName )
self.SpawnQueue[Client.UnitName] = true
self:T( "New AI Spawned for Client " .. Client.UnitName )
end
end
end
return true
end
)
self:__Monitor( 10 )
end

View File

@@ -1,541 +0,0 @@
--- **AI** - Perform Combat Air Patrolling (CAP) for airplanes.
--
-- **Features:**
--
-- * Patrol AI airplanes within a given zone.
-- * Trigger detected events when enemy airplanes are detected.
-- * Manage a fuel threshold to RTB on time.
-- * Engage the enemy when detected.
--
-- ===
--
-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AI/AI_CAP)
--
-- ===
--
-- ### [YouTube Playlist](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl1YCyPxJgoZn-CfhwyeW65L)
--
-- ===
--
-- ### Author: **FlightControl**
-- ### Contributions:
--
-- * **Quax**: Concept, Advice & Testing.
-- * **Pikey**: Concept, Advice & Testing.
-- * **Gunterlund**: Test case revision.
-- * **Whisper**: Testing.
-- * **Delta99**: Testing.
--
-- ===
--
-- @module AI.AI_CAP
-- @image AI_Combat_Air_Patrol.JPG
-- @type AI_CAP_ZONE
-- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Wrapper.Controllable} patrolling.
-- @field Core.Zone#ZONE_BASE TargetZone The @{Core.Zone} where the patrol needs to be executed.
-- @extends AI.AI_Patrol#AI_PATROL_ZONE
--- Implements the core functions to patrol a @{Core.Zone} by an AI @{Wrapper.Controllable} or @{Wrapper.Group}
-- and automatically engage any airborne enemies that are within a certain range or within a certain zone.
--
-- ![Process](..\Presentations\AI_CAP\Dia3.JPG)
--
-- The AI_CAP_ZONE is assigned a @{Wrapper.Group} and this must be done before the AI_CAP_ZONE process can be started using the **Start** event.
--
-- ![Process](..\Presentations\AI_CAP\Dia4.JPG)
--
-- The AI will fly towards the random 3D point within the patrol zone, using a random speed within the given altitude and speed limits.
-- Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits.
--
-- ![Process](..\Presentations\AI_CAP\Dia5.JPG)
--
-- This cycle will continue.
--
-- ![Process](..\Presentations\AI_CAP\Dia6.JPG)
--
-- During the patrol, the AI will detect enemy targets, which are reported through the **Detected** event.
--
-- ![Process](..\Presentations\AI_CAP\Dia9.JPG)
--
-- When enemies are detected, the AI will automatically engage the enemy.
--
-- ![Process](..\Presentations\AI_CAP\Dia10.JPG)
--
-- Until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
-- When the fuel threshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
--
-- ![Process](..\Presentations\AI_CAP\Dia13.JPG)
--
-- ## 1. AI_CAP_ZONE constructor
--
-- * @{#AI_CAP_ZONE.New}(): Creates a new AI_CAP_ZONE object.
--
-- ## 2. AI_CAP_ZONE is a FSM
--
-- ![Process](..\Presentations\AI_CAP\Dia2.JPG)
--
-- ### 2.1 AI_CAP_ZONE States
--
-- * **None** ( Group ): The process is not started yet.
-- * **Patrolling** ( Group ): The AI is patrolling the Patrol Zone.
-- * **Engaging** ( Group ): The AI is engaging the bogeys.
-- * **Returning** ( Group ): The AI is returning to Base..
--
-- ### 2.2 AI_CAP_ZONE Events
--
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Start}**: Start the process.
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Route}**: Route the AI to a new random 3D point within the Patrol Zone.
-- * **@{#AI_CAP_ZONE.Engage}**: Let the AI engage the bogeys.
-- * **@{#AI_CAP_ZONE.Abort}**: Aborts the engagement and return patrolling in the patrol zone.
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.RTB}**: Route the AI to the home base.
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detect}**: The AI is detecting targets.
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets.
-- * **@{#AI_CAP_ZONE.Destroy}**: The AI has destroyed a bogey @{Wrapper.Unit}.
-- * **@{#AI_CAP_ZONE.Destroyed}**: The AI has destroyed all bogeys @{Wrapper.Unit}s assigned in the CAS task.
-- * **Status** ( Group ): The AI is checking status (fuel and damage). When the thresholds have been reached, the AI will RTB.
--
-- ## 3. Set the Range of Engagement
--
-- ![Range](..\Presentations\AI_CAP\Dia11.JPG)
--
-- An optional range can be set in meters,
-- that will define when the AI will engage with the detected airborne enemy targets.
-- The range can be beyond or smaller than the range of the Patrol Zone.
-- The range is applied at the position of the AI.
-- Use the method @{#AI_CAP_ZONE.SetEngageRange}() to define that range.
--
-- ## 4. Set the Zone of Engagement
--
-- ![Zone](..\Presentations\AI_CAP\Dia12.JPG)
--
-- An optional @{Core.Zone} can be set,
-- that will define when the AI will engage with the detected airborne enemy targets.
-- Use the method @{#AI_CAP_ZONE.SetEngageZone}() to define that Zone.
--
-- # Developer Note
--
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
-- Therefore, this class is considered to be deprecated
--
-- ===
--
-- @field #AI_CAP_ZONE
AI_CAP_ZONE = {
ClassName = "AI_CAP_ZONE",
}
--- Creates a new AI_CAP_ZONE object
-- @param #AI_CAP_ZONE self
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Core.Zone} where the patrol needs to be executed.
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Controllable} in km/h.
-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Controllable} in km/h.
-- @param DCS#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO
-- @return #AI_CAP_ZONE self
function AI_CAP_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType )
-- Inherits from BASE
local self = BASE:Inherit( self, AI_PATROL_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType ) ) -- #AI_CAP_ZONE
self.Accomplished = false
self.Engaging = false
self:AddTransition( { "Patrolling", "Engaging" }, "Engage", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_CAP_ZONE.
--- OnBefore Transition Handler for Event Engage.
-- @function [parent=#AI_CAP_ZONE] OnBeforeEngage
-- @param #AI_CAP_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Engage.
-- @function [parent=#AI_CAP_ZONE] OnAfterEngage
-- @param #AI_CAP_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Engage.
-- @function [parent=#AI_CAP_ZONE] Engage
-- @param #AI_CAP_ZONE self
--- Asynchronous Event Trigger for Event Engage.
-- @function [parent=#AI_CAP_ZONE] __Engage
-- @param #AI_CAP_ZONE self
-- @param #number Delay The delay in seconds.
--- OnLeave Transition Handler for State Engaging.
-- @function [parent=#AI_CAP_ZONE] OnLeaveEngaging
-- @param #AI_CAP_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnEnter Transition Handler for State Engaging.
-- @function [parent=#AI_CAP_ZONE] OnEnterEngaging
-- @param #AI_CAP_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
self:AddTransition( "Engaging", "Fired", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_CAP_ZONE.
--- OnBefore Transition Handler for Event Fired.
-- @function [parent=#AI_CAP_ZONE] OnBeforeFired
-- @param #AI_CAP_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Fired.
-- @function [parent=#AI_CAP_ZONE] OnAfterFired
-- @param #AI_CAP_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Fired.
-- @function [parent=#AI_CAP_ZONE] Fired
-- @param #AI_CAP_ZONE self
--- Asynchronous Event Trigger for Event Fired.
-- @function [parent=#AI_CAP_ZONE] __Fired
-- @param #AI_CAP_ZONE self
-- @param #number Delay The delay in seconds.
self:AddTransition( "*", "Destroy", "*" ) -- FSM_CONTROLLABLE Transition for type #AI_CAP_ZONE.
--- OnBefore Transition Handler for Event Destroy.
-- @function [parent=#AI_CAP_ZONE] OnBeforeDestroy
-- @param #AI_CAP_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Destroy.
-- @function [parent=#AI_CAP_ZONE] OnAfterDestroy
-- @param #AI_CAP_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Destroy.
-- @function [parent=#AI_CAP_ZONE] Destroy
-- @param #AI_CAP_ZONE self
--- Asynchronous Event Trigger for Event Destroy.
-- @function [parent=#AI_CAP_ZONE] __Destroy
-- @param #AI_CAP_ZONE self
-- @param #number Delay The delay in seconds.
self:AddTransition( "Engaging", "Abort", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_CAP_ZONE.
--- OnBefore Transition Handler for Event Abort.
-- @function [parent=#AI_CAP_ZONE] OnBeforeAbort
-- @param #AI_CAP_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Abort.
-- @function [parent=#AI_CAP_ZONE] OnAfterAbort
-- @param #AI_CAP_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Abort.
-- @function [parent=#AI_CAP_ZONE] Abort
-- @param #AI_CAP_ZONE self
--- Asynchronous Event Trigger for Event Abort.
-- @function [parent=#AI_CAP_ZONE] __Abort
-- @param #AI_CAP_ZONE self
-- @param #number Delay The delay in seconds.
self:AddTransition( "Engaging", "Accomplish", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_CAP_ZONE.
--- OnBefore Transition Handler for Event Accomplish.
-- @function [parent=#AI_CAP_ZONE] OnBeforeAccomplish
-- @param #AI_CAP_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Accomplish.
-- @function [parent=#AI_CAP_ZONE] OnAfterAccomplish
-- @param #AI_CAP_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Accomplish.
-- @function [parent=#AI_CAP_ZONE] Accomplish
-- @param #AI_CAP_ZONE self
--- Asynchronous Event Trigger for Event Accomplish.
-- @function [parent=#AI_CAP_ZONE] __Accomplish
-- @param #AI_CAP_ZONE self
-- @param #number Delay The delay in seconds.
return self
end
--- Set the Engage Zone which defines where the AI will engage bogies.
-- @param #AI_CAP_ZONE self
-- @param Core.Zone#ZONE EngageZone The zone where the AI is performing CAP.
-- @return #AI_CAP_ZONE self
function AI_CAP_ZONE:SetEngageZone( EngageZone )
self:F2()
if EngageZone then
self.EngageZone = EngageZone
else
self.EngageZone = nil
end
end
--- Set the Engage Range when the AI will engage with airborne enemies.
-- @param #AI_CAP_ZONE self
-- @param #number EngageRange The Engage Range.
-- @return #AI_CAP_ZONE self
function AI_CAP_ZONE:SetEngageRange( EngageRange )
self:F2()
if EngageRange then
self.EngageRange = EngageRange
else
self.EngageRange = nil
end
end
--- onafter State Transition for Event Start.
-- @param #AI_CAP_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_CAP_ZONE:onafterStart( Controllable, From, Event, To )
-- Call the parent Start event handler
self:GetParent(self).onafterStart( self, Controllable, From, Event, To )
self:HandleEvent( EVENTS.Dead )
end
-- @param AI.AI_CAP#AI_CAP_ZONE
-- @param Wrapper.Group#GROUP EngageGroup
function AI_CAP_ZONE.EngageRoute( EngageGroup, Fsm )
EngageGroup:F( { "AI_CAP_ZONE.EngageRoute:", EngageGroup:GetName() } )
if EngageGroup:IsAlive() then
Fsm:__Engage( 1 )
end
end
-- @param #AI_CAP_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_CAP_ZONE:onbeforeEngage( Controllable, From, Event, To )
if self.Accomplished == true then
return false
end
end
-- @param #AI_CAP_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_CAP_ZONE:onafterDetected( Controllable, From, Event, To )
if From ~= "Engaging" then
local Engage = false
for DetectedUnit, Detected in pairs( self.DetectedUnits ) do
local DetectedUnit = DetectedUnit -- Wrapper.Unit#UNIT
self:T( DetectedUnit )
if DetectedUnit:IsAlive() and DetectedUnit:IsAir() then
Engage = true
break
end
end
if Engage == true then
self:F( 'Detected -> Engaging' )
self:__Engage( 1 )
end
end
end
-- @param #AI_CAP_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_CAP_ZONE:onafterAbort( Controllable, From, Event, To )
Controllable:ClearTasks()
self:__Route( 1 )
end
-- @param #AI_CAP_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_CAP_ZONE:onafterEngage( Controllable, From, Event, To )
if Controllable and Controllable:IsAlive() then
local EngageRoute = {}
--- Calculate the current route point.
local CurrentVec2 = self.Controllable:GetVec2()
if not CurrentVec2 then return self end
--DONE: Create GetAltitude function for GROUP, and delete GetUnit(1).
local CurrentAltitude = self.Controllable:GetAltitude()
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
local ToEngageZoneSpeed = self.PatrolMaxSpeed
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
self.PatrolAltType,
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
ToEngageZoneSpeed,
true
)
EngageRoute[#EngageRoute+1] = CurrentRoutePoint
--- Find a random 2D point in PatrolZone.
local ToTargetVec2 = self.PatrolZone:GetRandomVec2()
self:T2( ToTargetVec2 )
--- Define Speed and Altitude.
local ToTargetAltitude = math.random( self.EngageFloorAltitude, self.EngageCeilingAltitude )
local ToTargetSpeed = math.random( self.PatrolMinSpeed, self.PatrolMaxSpeed )
self:T2( { self.PatrolMinSpeed, self.PatrolMaxSpeed, ToTargetSpeed } )
--- Obtain a 3D @{Point} from the 2D point + altitude.
local ToTargetPointVec3 = POINT_VEC3:New( ToTargetVec2.x, ToTargetAltitude, ToTargetVec2.y )
--- Create a route point of type air.
local ToPatrolRoutePoint = ToTargetPointVec3:WaypointAir(
self.PatrolAltType,
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
ToTargetSpeed,
true
)
EngageRoute[#EngageRoute+1] = ToPatrolRoutePoint
Controllable:OptionROEOpenFire()
Controllable:OptionROTEvadeFire()
local AttackTasks = {}
for DetectedUnit, Detected in pairs( self.DetectedUnits ) do
local DetectedUnit = DetectedUnit -- Wrapper.Unit#UNIT
self:T( { DetectedUnit, DetectedUnit:IsAlive(), DetectedUnit:IsAir() } )
if DetectedUnit:IsAlive() and DetectedUnit:IsAir() then
if self.EngageZone then
if DetectedUnit:IsInZone( self.EngageZone ) then
self:F( {"Within Zone and Engaging ", DetectedUnit } )
AttackTasks[#AttackTasks+1] = Controllable:TaskAttackUnit( DetectedUnit )
end
else
if self.EngageRange then
if DetectedUnit:GetPointVec3():Get2DDistance(Controllable:GetPointVec3() ) <= self.EngageRange then
self:F( {"Within Range and Engaging", DetectedUnit } )
AttackTasks[#AttackTasks+1] = Controllable:TaskAttackUnit( DetectedUnit )
end
else
AttackTasks[#AttackTasks+1] = Controllable:TaskAttackUnit( DetectedUnit )
end
end
else
self.DetectedUnits[DetectedUnit] = nil
end
end
if #AttackTasks == 0 then
self:F("No targets found -> Going back to Patrolling")
self:__Abort( 1 )
self:__Route( 1 )
self:SetDetectionActivated()
else
AttackTasks[#AttackTasks+1] = Controllable:TaskFunction( "AI_CAP_ZONE.EngageRoute", self )
EngageRoute[1].task = Controllable:TaskCombo( AttackTasks )
self:SetDetectionDeactivated()
end
Controllable:Route( EngageRoute, 0.5 )
end
end
-- @param #AI_CAP_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_CAP_ZONE:onafterAccomplish( Controllable, From, Event, To )
self.Accomplished = true
self:SetDetectionOff()
end
-- @param #AI_CAP_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @param Core.Event#EVENTDATA EventData
function AI_CAP_ZONE:onafterDestroy( Controllable, From, Event, To, EventData )
if EventData.IniUnit then
self.DetectedUnits[EventData.IniUnit] = nil
end
end
-- @param #AI_CAP_ZONE self
-- @param Core.Event#EVENTDATA EventData
function AI_CAP_ZONE:OnEventDead( EventData )
self:F( { "EventDead", EventData } )
if EventData.IniDCSUnit then
if self.DetectedUnits and self.DetectedUnits[EventData.IniUnit] then
self:__Destroy( 1, EventData )
end
end
end

View File

@@ -1,570 +0,0 @@
--- **AI** - Perform Close Air Support (CAS) near friendlies.
--
-- **Features:**
--
-- * Hold and standby within a patrol zone.
-- * Engage upon command the enemies within an engagement zone.
-- * Loop the zone until all enemies are eliminated.
-- * Trigger different events upon the results achieved.
-- * After combat, return to the patrol zone and hold.
-- * RTB when commanded or after fuel.
--
-- ===
--
-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AI/AI_CAS)
--
-- ===
--
-- ### [YouTube Playlist](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl3JBO1WDqqpyYRRmIkR2ir2)
--
-- ===
--
-- ### Author: **FlightControl**
-- ### Contributions:
--
-- * **Quax**: Concept, Advice & Testing.
-- * **Pikey**: Concept, Advice & Testing.
-- * **Gunterlund**: Test case revision.
--
-- ===
--
-- @module AI.AI_CAS
-- @image AI_Close_Air_Support.JPG
--- AI_CAS_ZONE class
-- @type AI_CAS_ZONE
-- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Wrapper.Controllable} patrolling.
-- @field Core.Zone#ZONE_BASE TargetZone The @{Core.Zone} where the patrol needs to be executed.
-- @extends AI.AI_Patrol#AI_PATROL_ZONE
--- Implements the core functions to provide Close Air Support in an Engage @{Core.Zone} by an AIR @{Wrapper.Controllable} or @{Wrapper.Group}.
-- The AI_CAS_ZONE runs a process. It holds an AI in a Patrol Zone and when the AI is commanded to engage, it will fly to an Engage Zone.
--
-- ![HoldAndEngage](..\Presentations\AI_CAS\Dia3.JPG)
--
-- The AI_CAS_ZONE is assigned a @{Wrapper.Group} and this must be done before the AI_CAS_ZONE process can be started through the **Start** event.
--
-- ![Start Event](..\Presentations\AI_CAS\Dia4.JPG)
--
-- Upon started, The AI will **Route** itself towards the random 3D point within a patrol zone,
-- using a random speed within the given altitude and speed limits.
-- Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits.
-- This cycle will continue until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
--
-- ![Route Event](..\Presentations\AI_CAS\Dia5.JPG)
--
-- When the AI is commanded to provide Close Air Support (through the event **Engage**), the AI will fly towards the Engage Zone.
-- Any target that is detected in the Engage Zone will be reported and will be destroyed by the AI.
--
-- ![Engage Event](..\Presentations\AI_CAS\Dia6.JPG)
--
-- The AI will detect the targets and will only destroy the targets within the Engage Zone.
--
-- ![Engage Event](..\Presentations\AI_CAS\Dia7.JPG)
--
-- Every target that is destroyed, is reported< by the AI.
--
-- ![Engage Event](..\Presentations\AI_CAS\Dia8.JPG)
--
-- Note that the AI does not know when the Engage Zone is cleared, and therefore will keep circling in the zone.
--
-- ![Engage Event](..\Presentations\AI_CAS\Dia9.JPG)
--
-- Until it is notified through the event **Accomplish**, which is to be triggered by an observing party:
--
-- * a FAC
-- * a timed event
-- * a menu option selected by a human
-- * a condition
-- * others ...
--
-- ![Engage Event](..\Presentations\AI_CAS\Dia10.JPG)
--
-- When the AI has accomplished the CAS, it will fly back to the Patrol Zone.
--
-- ![Engage Event](..\Presentations\AI_CAS\Dia11.JPG)
--
-- It will keep patrolling there, until it is notified to RTB or move to another CAS Zone.
-- It can be notified to go RTB through the **RTB** event.
--
-- When the fuel threshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
--
-- ![Engage Event](..\Presentations\AI_CAS\Dia12.JPG)
--
-- ## AI_CAS_ZONE constructor
--
-- * @{#AI_CAS_ZONE.New}(): Creates a new AI_CAS_ZONE object.
--
-- ## AI_CAS_ZONE is a FSM
--
-- ![Process](..\Presentations\AI_CAS\Dia2.JPG)
--
-- ### 2.1. AI_CAS_ZONE States
--
-- * **None** ( Group ): The process is not started yet.
-- * **Patrolling** ( Group ): The AI is patrolling the Patrol Zone.
-- * **Engaging** ( Group ): The AI is engaging the targets in the Engage Zone, executing CAS.
-- * **Returning** ( Group ): The AI is returning to Base..
--
-- ### 2.2. AI_CAS_ZONE Events
--
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Start}**: Start the process.
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Route}**: Route the AI to a new random 3D point within the Patrol Zone.
-- * **@{#AI_CAS_ZONE.Engage}**: Engage the AI to provide CAS in the Engage Zone, destroying any target it finds.
-- * **@{#AI_CAS_ZONE.Abort}**: Aborts the engagement and return patrolling in the patrol zone.
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.RTB}**: Route the AI to the home base.
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detect}**: The AI is detecting targets.
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets.
-- * **@{#AI_CAS_ZONE.Destroy}**: The AI has destroyed a target @{Wrapper.Unit}.
-- * **@{#AI_CAS_ZONE.Destroyed}**: The AI has destroyed all target @{Wrapper.Unit}s assigned in the CAS task.
-- * **Status**: The AI is checking status (fuel and damage). When the thresholds have been reached, the AI will RTB.
--
-- # Developer Note
--
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
-- Therefore, this class is considered to be deprecated
--
-- ===
--
-- @field #AI_CAS_ZONE
AI_CAS_ZONE = {
ClassName = "AI_CAS_ZONE",
}
--- Creates a new AI_CAS_ZONE object
-- @param #AI_CAS_ZONE self
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Core.Zone} where the patrol needs to be executed.
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Controllable} in km/h.
-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Controllable} in km/h.
-- @param Core.Zone#ZONE_BASE EngageZone The zone where the engage will happen.
-- @param DCS#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO
-- @return #AI_CAS_ZONE self
function AI_CAS_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, EngageZone, PatrolAltType )
-- Inherits from BASE
local self = BASE:Inherit( self, AI_PATROL_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType ) ) -- #AI_CAS_ZONE
self.EngageZone = EngageZone
self.Accomplished = false
self:SetDetectionZone( self.EngageZone )
self:AddTransition( { "Patrolling", "Engaging" }, "Engage", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_CAS_ZONE.
--- OnBefore Transition Handler for Event Engage.
-- @function [parent=#AI_CAS_ZONE] OnBeforeEngage
-- @param #AI_CAS_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Engage.
-- @function [parent=#AI_CAS_ZONE] OnAfterEngage
-- @param #AI_CAS_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Engage.
-- @function [parent=#AI_CAS_ZONE] Engage
-- @param #AI_CAS_ZONE self
-- @param #number EngageSpeed (optional) The speed the Group will hold when engaging to the target zone.
-- @param DCS#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement.
-- @param DCS#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack.
-- If parameter is not defined the unit / controllable will choose expend on its own discretion.
-- Use the structure @{DCS#AI.Task.WeaponExpend} to define the amount of weapons to be release at each attack.
-- @param #number EngageAttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo.
-- @param DCS#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction.
--- Asynchronous Event Trigger for Event Engage.
-- @function [parent=#AI_CAS_ZONE] __Engage
-- @param #AI_CAS_ZONE self
-- @param #number Delay The delay in seconds.
-- @param #number EngageSpeed (optional) The speed the Group will hold when engaging to the target zone.
-- @param DCS#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement.
-- @param DCS#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack.
-- If parameter is not defined the unit / controllable will choose expend on its own discretion.
-- Use the structure @{DCS#AI.Task.WeaponExpend} to define the amount of weapons to be release at each attack.
-- @param #number EngageAttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo.
-- @param DCS#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction.
--- OnLeave Transition Handler for State Engaging.
-- @function [parent=#AI_CAS_ZONE] OnLeaveEngaging
-- @param #AI_CAS_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnEnter Transition Handler for State Engaging.
-- @function [parent=#AI_CAS_ZONE] OnEnterEngaging
-- @param #AI_CAS_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
self:AddTransition( "Engaging", "Target", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_CAS_ZONE.
self:AddTransition( "Engaging", "Fired", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_CAS_ZONE.
--- OnBefore Transition Handler for Event Fired.
-- @function [parent=#AI_CAS_ZONE] OnBeforeFired
-- @param #AI_CAS_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Fired.
-- @function [parent=#AI_CAS_ZONE] OnAfterFired
-- @param #AI_CAS_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Fired.
-- @function [parent=#AI_CAS_ZONE] Fired
-- @param #AI_CAS_ZONE self
--- Asynchronous Event Trigger for Event Fired.
-- @function [parent=#AI_CAS_ZONE] __Fired
-- @param #AI_CAS_ZONE self
-- @param #number Delay The delay in seconds.
self:AddTransition( "*", "Destroy", "*" ) -- FSM_CONTROLLABLE Transition for type #AI_CAS_ZONE.
--- OnBefore Transition Handler for Event Destroy.
-- @function [parent=#AI_CAS_ZONE] OnBeforeDestroy
-- @param #AI_CAS_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Destroy.
-- @function [parent=#AI_CAS_ZONE] OnAfterDestroy
-- @param #AI_CAS_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Destroy.
-- @function [parent=#AI_CAS_ZONE] Destroy
-- @param #AI_CAS_ZONE self
--- Asynchronous Event Trigger for Event Destroy.
-- @function [parent=#AI_CAS_ZONE] __Destroy
-- @param #AI_CAS_ZONE self
-- @param #number Delay The delay in seconds.
self:AddTransition( "Engaging", "Abort", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_CAS_ZONE.
--- OnBefore Transition Handler for Event Abort.
-- @function [parent=#AI_CAS_ZONE] OnBeforeAbort
-- @param #AI_CAS_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Abort.
-- @function [parent=#AI_CAS_ZONE] OnAfterAbort
-- @param #AI_CAS_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Abort.
-- @function [parent=#AI_CAS_ZONE] Abort
-- @param #AI_CAS_ZONE self
--- Asynchronous Event Trigger for Event Abort.
-- @function [parent=#AI_CAS_ZONE] __Abort
-- @param #AI_CAS_ZONE self
-- @param #number Delay The delay in seconds.
self:AddTransition( "Engaging", "Accomplish", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_CAS_ZONE.
--- OnBefore Transition Handler for Event Accomplish.
-- @function [parent=#AI_CAS_ZONE] OnBeforeAccomplish
-- @param #AI_CAS_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Accomplish.
-- @function [parent=#AI_CAS_ZONE] OnAfterAccomplish
-- @param #AI_CAS_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Accomplish.
-- @function [parent=#AI_CAS_ZONE] Accomplish
-- @param #AI_CAS_ZONE self
--- Asynchronous Event Trigger for Event Accomplish.
-- @function [parent=#AI_CAS_ZONE] __Accomplish
-- @param #AI_CAS_ZONE self
-- @param #number Delay The delay in seconds.
return self
end
--- Set the Engage Zone where the AI is performing CAS. Note that if the EngageZone is changed, the AI needs to re-detect targets.
-- @param #AI_CAS_ZONE self
-- @param Core.Zone#ZONE EngageZone The zone where the AI is performing CAS.
-- @return #AI_CAS_ZONE self
function AI_CAS_ZONE:SetEngageZone( EngageZone )
self:F2()
if EngageZone then
self.EngageZone = EngageZone
else
self.EngageZone = nil
end
end
--- onafter State Transition for Event Start.
-- @param #AI_CAS_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_CAS_ZONE:onafterStart( Controllable, From, Event, To )
-- Call the parent Start event handler
self:GetParent(self).onafterStart( self, Controllable, From, Event, To )
self:HandleEvent( EVENTS.Dead )
self:SetDetectionDeactivated() -- When not engaging, set the detection off.
end
-- @param AI.AI_CAS#AI_CAS_ZONE
-- @param Wrapper.Group#GROUP EngageGroup
function AI_CAS_ZONE.EngageRoute( EngageGroup, Fsm )
EngageGroup:F( { "AI_CAS_ZONE.EngageRoute:", EngageGroup:GetName() } )
if EngageGroup:IsAlive() then
Fsm:__Engage( 1, Fsm.EngageSpeed, Fsm.EngageAltitude, Fsm.EngageWeaponExpend, Fsm.EngageAttackQty, Fsm.EngageDirection )
end
end
-- @param #AI_CAS_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_CAS_ZONE:onbeforeEngage( Controllable, From, Event, To )
if self.Accomplished == true then
return false
end
end
-- @param #AI_CAS_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_CAS_ZONE:onafterTarget( Controllable, From, Event, To )
if Controllable:IsAlive() then
local AttackTasks = {}
for DetectedUnit, Detected in pairs( self.DetectedUnits ) do
local DetectedUnit = DetectedUnit -- Wrapper.Unit#UNIT
if DetectedUnit:IsAlive() then
if DetectedUnit:IsInZone( self.EngageZone ) then
if Detected == true then
self:F( {"Target: ", DetectedUnit } )
self.DetectedUnits[DetectedUnit] = false
local AttackTask = Controllable:TaskAttackUnit( DetectedUnit, false, self.EngageWeaponExpend, self.EngageAttackQty, self.EngageDirection, self.EngageAltitude, nil )
self.Controllable:PushTask( AttackTask, 1 )
end
end
else
self.DetectedUnits[DetectedUnit] = nil
end
end
self:__Target( -10 )
end
end
-- @param #AI_CAS_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_CAS_ZONE:onafterAbort( Controllable, From, Event, To )
Controllable:ClearTasks()
self:__Route( 1 )
end
-- @param #AI_CAS_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @param #number EngageSpeed (optional) The speed the Group will hold when engaging to the target zone.
-- @param DCS#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement.
-- @param DCS#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion.
-- @param #number EngageAttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo.
-- @param DCS#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction.
function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To,
EngageSpeed,
EngageAltitude,
EngageWeaponExpend,
EngageAttackQty,
EngageDirection )
self:F("onafterEngage")
self.EngageSpeed = EngageSpeed or 400
self.EngageAltitude = EngageAltitude or 2000
self.EngageWeaponExpend = EngageWeaponExpend
self.EngageAttackQty = EngageAttackQty
self.EngageDirection = EngageDirection
if Controllable:IsAlive() then
Controllable:OptionROEOpenFire()
Controllable:OptionROTVertical()
local EngageRoute = {}
--- Calculate the current route point.
local CurrentVec2 = self.Controllable:GetVec2()
--DONE: Create GetAltitude function for GROUP, and delete GetUnit(1).
local CurrentAltitude = self.Controllable:GetAltitude()
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
local ToEngageZoneSpeed = self.PatrolMaxSpeed
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
self.PatrolAltType,
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
self.EngageSpeed,
true
)
EngageRoute[#EngageRoute+1] = CurrentRoutePoint
local AttackTasks = {}
for DetectedUnit, Detected in pairs( self.DetectedUnits ) do
local DetectedUnit = DetectedUnit -- Wrapper.Unit#UNIT
self:T( DetectedUnit )
if DetectedUnit:IsAlive() then
if DetectedUnit:IsInZone( self.EngageZone ) then
self:F( {"Engaging ", DetectedUnit } )
AttackTasks[#AttackTasks+1] = Controllable:TaskAttackUnit( DetectedUnit,
true,
EngageWeaponExpend,
EngageAttackQty,
EngageDirection
)
end
else
self.DetectedUnits[DetectedUnit] = nil
end
end
AttackTasks[#AttackTasks+1] = Controllable:TaskFunction( "AI_CAS_ZONE.EngageRoute", self )
EngageRoute[#EngageRoute].task = Controllable:TaskCombo( AttackTasks )
--- Define a random point in the @{Core.Zone}. The AI will fly to that point within the zone.
--- Find a random 2D point in EngageZone.
local ToTargetVec2 = self.EngageZone:GetRandomVec2()
self:T2( ToTargetVec2 )
--- Obtain a 3D @{Point} from the 2D point + altitude.
local ToTargetPointVec3 = POINT_VEC3:New( ToTargetVec2.x, self.EngageAltitude, ToTargetVec2.y )
--- Create a route point of type air.
local ToTargetRoutePoint = ToTargetPointVec3:WaypointAir(
self.PatrolAltType,
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
self.EngageSpeed,
true
)
EngageRoute[#EngageRoute+1] = ToTargetRoutePoint
Controllable:Route( EngageRoute, 0.5 )
self:SetRefreshTimeInterval( 2 )
self:SetDetectionActivated()
self:__Target( -2 ) -- Start targeting
end
end
-- @param #AI_CAS_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_CAS_ZONE:onafterAccomplish( Controllable, From, Event, To )
self.Accomplished = true
self:SetDetectionDeactivated()
end
-- @param #AI_CAS_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @param Core.Event#EVENTDATA EventData
function AI_CAS_ZONE:onafterDestroy( Controllable, From, Event, To, EventData )
if EventData.IniUnit then
self.DetectedUnits[EventData.IniUnit] = nil
end
end
-- @param #AI_CAS_ZONE self
-- @param Core.Event#EVENTDATA EventData
function AI_CAS_ZONE:OnEventDead( EventData )
self:F( { "EventDead", EventData } )
if EventData.IniDCSUnit then
if self.DetectedUnits and self.DetectedUnits[EventData.IniUnit] then
self:__Destroy( 1, EventData )
end
end
end

View File

@@ -1,589 +0,0 @@
--- **AI** - Models the intelligent transportation of infantry and other cargo.
--
-- ===
--
-- ### Author: **FlightControl**
--
-- ===
--
-- @module AI.AI_Cargo
-- @image Cargo.JPG
-- @type AI_CARGO
-- @extends Core.Fsm#FSM_CONTROLLABLE
--- Base class for the dynamic cargo handling capability for AI groups.
--
-- Carriers can be mobilized to intelligently transport infantry and other cargo within the simulation.
-- The AI_CARGO module uses the @{Cargo.Cargo} capabilities within the MOOSE framework.
-- CARGO derived objects must be declared within the mission to make the AI_CARGO object recognize the cargo.
-- Please consult the @{Cargo.Cargo} module for more information.
--
-- The derived classes from this module are:
--
-- * @{AI.AI_Cargo_APC} - Cargo transportation using APCs and other vehicles between zones.
-- * @{AI.AI_Cargo_Helicopter} - Cargo transportation using helicopters between zones.
-- * @{AI.AI_Cargo_Airplane} - Cargo transportation using airplanes to and from airbases.
--
-- # Developer Note
--
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
-- Therefore, this class is considered to be deprecated
--
-- @field #AI_CARGO
AI_CARGO = {
ClassName = "AI_CARGO",
Coordinate = nil, -- Core.Point#COORDINATE,
Carrier_Cargo = {},
}
--- Creates a new AI_CARGO object.
-- @param #AI_CARGO self
-- @param Wrapper.Group#GROUP Carrier Cargo carrier group.
-- @param Core.Set#SET_CARGO CargoSet Set of cargo(s) to transport.
-- @return #AI_CARGO self
function AI_CARGO:New( Carrier, CargoSet )
local self = BASE:Inherit( self, FSM_CONTROLLABLE:New( Carrier ) ) -- #AI_CARGO
self.CargoSet = CargoSet -- Core.Set#SET_CARGO
self.CargoCarrier = Carrier -- Wrapper.Group#GROUP
self:SetStartState( "Unloaded" )
-- Board
self:AddTransition( "Unloaded", "Pickup", "Unloaded" )
self:AddTransition( "*", "Load", "*" )
self:AddTransition( "*", "Reload", "*" )
self:AddTransition( "*", "Board", "*" )
self:AddTransition( "*", "Loaded", "Loaded" )
self:AddTransition( "Loaded", "PickedUp", "Loaded" )
-- Unload
self:AddTransition( "Loaded", "Deploy", "*" )
self:AddTransition( "*", "Unload", "*" )
self:AddTransition( "*", "Unboard", "*" )
self:AddTransition( "*", "Unloaded", "Unloaded" )
self:AddTransition( "Unloaded", "Deployed", "Unloaded" )
--- Pickup Handler OnBefore for AI_CARGO
-- @function [parent=#AI_CARGO] OnBeforePickup
-- @param #AI_CARGO self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param Core.Point#COORDINATE Coordinate
-- @param #number Speed Speed in km/h. Default is 50% of max possible speed the group can do.
-- @return #boolean
--- Pickup Handler OnAfter for AI_CARGO
-- @function [parent=#AI_CARGO] OnAfterPickup
-- @param #AI_CARGO self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param Core.Point#COORDINATE Coordinate
-- @param #number Speed Speed in km/h. Default is 50% of max possible speed the group can do.
--- Pickup Trigger for AI_CARGO
-- @function [parent=#AI_CARGO] Pickup
-- @param #AI_CARGO self
-- @param Core.Point#COORDINATE Coordinate
-- @param #number Speed Speed in km/h. Default is 50% of max possible speed the group can do.
--- Pickup Asynchronous Trigger for AI_CARGO
-- @function [parent=#AI_CARGO] __Pickup
-- @param #AI_CARGO self
-- @param #number Delay
-- @param Core.Point#COORDINATE Coordinate Pickup place. If not given, loading starts at the current location.
-- @param #number Speed Speed in km/h. Default is 50% of max possible speed the group can do.
--- Deploy Handler OnBefore for AI_CARGO
-- @function [parent=#AI_CARGO] OnBeforeDeploy
-- @param #AI_CARGO self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param Core.Point#COORDINATE Coordinate
-- @param #number Speed Speed in km/h. Default is 50% of max possible speed the group can do.
-- @return #boolean
--- Deploy Handler OnAfter for AI_CARGO
-- @function [parent=#AI_CARGO] OnAfterDeploy
-- @param #AI_CARGO self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param Core.Point#COORDINATE Coordinate
-- @param #number Speed Speed in km/h. Default is 50% of max possible speed the group can do.
--- Deploy Trigger for AI_CARGO
-- @function [parent=#AI_CARGO] Deploy
-- @param #AI_CARGO self
-- @param Core.Point#COORDINATE Coordinate
-- @param #number Speed Speed in km/h. Default is 50% of max possible speed the group can do.
--- Deploy Asynchronous Trigger for AI_CARGO
-- @function [parent=#AI_CARGO] __Deploy
-- @param #AI_CARGO self
-- @param #number Delay
-- @param Core.Point#COORDINATE Coordinate
-- @param #number Speed Speed in km/h. Default is 50% of max possible speed the group can do.
--- Loaded Handler OnAfter for AI_CARGO
-- @function [parent=#AI_CARGO] OnAfterLoaded
-- @param #AI_CARGO self
-- @param Wrapper.Group#GROUP Carrier
-- @param #string From
-- @param #string Event
-- @param #string To
--- Unloaded Handler OnAfter for AI_CARGO
-- @function [parent=#AI_CARGO] OnAfterUnloaded
-- @param #AI_CARGO self
-- @param Wrapper.Group#GROUP Carrier
-- @param #string From
-- @param #string Event
-- @param #string To
--- On after Deployed event.
-- @function [parent=#AI_CARGO] OnAfterDeployed
-- @param #AI_CARGO self
-- @param Wrapper.Group#GROUP Carrier
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE.
-- @param #boolean Defend Defend for APCs.
for _, CarrierUnit in pairs( Carrier:GetUnits() ) do
local CarrierUnit = CarrierUnit -- Wrapper.Unit#UNIT
CarrierUnit:SetCargoBayWeightLimit()
end
self.Transporting = false
self.Relocating = false
return self
end
function AI_CARGO:IsTransporting()
return self.Transporting == true
end
function AI_CARGO:IsRelocating()
return self.Relocating == true
end
--- On after Pickup event.
-- @param #AI_CARGO self
-- @param Wrapper.Group#GROUP APC
-- @param From
-- @param Event
-- @param To
-- @param Core.Point#COORDINATE Coordinate of the pickup point.
-- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go.
-- @param #number Height Height in meters to move to the home coordinate.
-- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided.
function AI_CARGO:onafterPickup( APC, From, Event, To, Coordinate, Speed, Height, PickupZone )
self.Transporting = false
self.Relocating = true
end
--- On after Deploy event.
-- @param #AI_CARGO self
-- @param Wrapper.Group#GROUP APC
-- @param From
-- @param Event
-- @param To
-- @param Core.Point#COORDINATE Coordinate Deploy place.
-- @param #number Speed Speed in km/h to drive to the depoly coordinate. Default is 50% of max possible speed the unit can go.
-- @param #number Height Height in meters to move to the deploy coordinate.
-- @param Core.Zone#ZONE DeployZone The zone where the cargo will be deployed.
function AI_CARGO:onafterDeploy( APC, From, Event, To, Coordinate, Speed, Height, DeployZone )
self.Relocating = false
self.Transporting = true
end
--- On before Load event.
-- @param #AI_CARGO self
-- @param Wrapper.Group#GROUP Carrier
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided.
function AI_CARGO:onbeforeLoad( Carrier, From, Event, To, PickupZone )
self:F( { Carrier, From, Event, To } )
local Boarding = false
local LoadInterval = 2
local LoadDelay = 1
local Carrier_List = {}
local Carrier_Weight = {}
if Carrier and Carrier:IsAlive() then
self.Carrier_Cargo = {}
for _, CarrierUnit in pairs( Carrier:GetUnits() ) do
local CarrierUnit = CarrierUnit -- Wrapper.Unit#UNIT
local CargoBayFreeWeight = CarrierUnit:GetCargoBayFreeWeight()
self:F({CargoBayFreeWeight=CargoBayFreeWeight})
Carrier_List[#Carrier_List+1] = CarrierUnit
Carrier_Weight[CarrierUnit] = CargoBayFreeWeight
end
local Carrier_Count = #Carrier_List
local Carrier_Index = 1
local Loaded = false
for _, Cargo in UTILS.spairs( self.CargoSet:GetSet(), function( t, a, b ) return t[a]:GetWeight() > t[b]:GetWeight() end ) do
local Cargo = Cargo -- Cargo.Cargo#CARGO
self:F( { IsUnLoaded = Cargo:IsUnLoaded(), IsDeployed = Cargo:IsDeployed(), Cargo:GetName(), Carrier:GetName() } )
-- Try all Carriers, but start from the one according the Carrier_Index
for Carrier_Loop = 1, #Carrier_List do
local CarrierUnit = Carrier_List[Carrier_Index] -- Wrapper.Unit#UNIT
-- This counters loop through the available Carriers.
Carrier_Index = Carrier_Index + 1
if Carrier_Index > Carrier_Count then
Carrier_Index = 1
end
if Cargo:IsUnLoaded() and not Cargo:IsDeployed() then
if Cargo:IsInLoadRadius( CarrierUnit:GetCoordinate() ) then
self:F( { "In radius", CarrierUnit:GetName() } )
local CargoWeight = Cargo:GetWeight()
local CarrierSpace=Carrier_Weight[CarrierUnit]
-- Only when there is space within the bay to load the next cargo item!
if CarrierSpace > CargoWeight then
Carrier:RouteStop()
--Cargo:Ungroup()
Cargo:__Board( -LoadDelay, CarrierUnit )
self:__Board( LoadDelay, Cargo, CarrierUnit, PickupZone )
LoadDelay = LoadDelay + Cargo:GetCount() * LoadInterval
-- So now this CarrierUnit has Cargo that is being loaded.
-- This will be used further in the logic to follow and to check cargo status.
self.Carrier_Cargo[Cargo] = CarrierUnit
Boarding = true
Carrier_Weight[CarrierUnit] = Carrier_Weight[CarrierUnit] - CargoWeight
Loaded = true
-- Ok, we loaded a cargo, now we can stop the loop.
break
else
self:T(string.format("WARNING: Cargo too heavy for carrier %s. Cargo=%.1f > %.1f free space", tostring(CarrierUnit:GetName()), CargoWeight, CarrierSpace))
end
end
end
end
end
if not Loaded == true then
-- No loading happened, so we need to pickup something else.
self.Relocating = false
end
end
return Boarding
end
--- On before Reload event.
-- @param #AI_CARGO self
-- @param Wrapper.Group#GROUP Carrier
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided.
function AI_CARGO:onbeforeReload( Carrier, From, Event, To )
self:F( { Carrier, From, Event, To } )
local Boarding = false
local LoadInterval = 2
local LoadDelay = 1
local Carrier_List = {}
local Carrier_Weight = {}
if Carrier and Carrier:IsAlive() then
for _, CarrierUnit in pairs( Carrier:GetUnits() ) do
local CarrierUnit = CarrierUnit -- Wrapper.Unit#UNIT
Carrier_List[#Carrier_List+1] = CarrierUnit
end
local Carrier_Count = #Carrier_List
local Carrier_Index = 1
local Loaded = false
for Cargo, CarrierUnit in pairs( self.Carrier_Cargo ) do
local Cargo = Cargo -- Cargo.Cargo#CARGO
self:F( { IsUnLoaded = Cargo:IsUnLoaded(), IsDeployed = Cargo:IsDeployed(), Cargo:GetName(), Carrier:GetName() } )
-- Try all Carriers, but start from the one according the Carrier_Index
for Carrier_Loop = 1, #Carrier_List do
local CarrierUnit = Carrier_List[Carrier_Index] -- Wrapper.Unit#UNIT
-- This counters loop through the available Carriers.
Carrier_Index = Carrier_Index + 1
if Carrier_Index > Carrier_Count then
Carrier_Index = 1
end
if Cargo:IsUnLoaded() and not Cargo:IsDeployed() then
Carrier:RouteStop()
Cargo:__Board( -LoadDelay, CarrierUnit )
self:__Board( LoadDelay, Cargo, CarrierUnit )
LoadDelay = LoadDelay + Cargo:GetCount() * LoadInterval
-- So now this CarrierUnit has Cargo that is being loaded.
-- This will be used further in the logic to follow and to check cargo status.
self.Carrier_Cargo[Cargo] = CarrierUnit
Boarding = true
Loaded = true
end
end
end
if not Loaded == true then
-- No loading happened, so we need to pickup something else.
self.Relocating = false
end
end
return Boarding
end
--- On after Board event.
-- @param #AI_CARGO self
-- @param Wrapper.Group#GROUP Carrier
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Cargo.Cargo#CARGO Cargo Cargo object.
-- @param Wrapper.Unit#UNIT CarrierUnit
-- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided.
function AI_CARGO:onafterBoard( Carrier, From, Event, To, Cargo, CarrierUnit, PickupZone )
self:F( { Carrier, From, Event, To, Cargo, CarrierUnit:GetName() } )
if Carrier and Carrier:IsAlive() then
self:F({ IsLoaded = Cargo:IsLoaded(), Cargo:GetName(), Carrier:GetName() } )
if not Cargo:IsLoaded() and not Cargo:IsDestroyed() then
self:__Board( -10, Cargo, CarrierUnit, PickupZone )
return
end
end
self:__Loaded( 0.1, Cargo, CarrierUnit, PickupZone )
end
--- On after Loaded event.
-- @param #AI_CARGO self
-- @param Wrapper.Group#GROUP Carrier
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @return #boolean Cargo loaded.
-- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided.
function AI_CARGO:onafterLoaded( Carrier, From, Event, To, Cargo, PickupZone )
self:F( { Carrier, From, Event, To } )
local Loaded = true
if Carrier and Carrier:IsAlive() then
for Cargo, CarrierUnit in pairs( self.Carrier_Cargo ) do
local Cargo = Cargo -- Cargo.Cargo#CARGO
self:F( { IsLoaded = Cargo:IsLoaded(), IsDestroyed = Cargo:IsDestroyed(), Cargo:GetName(), Carrier:GetName() } )
if not Cargo:IsLoaded() and not Cargo:IsDestroyed() then
Loaded = false
end
end
end
if Loaded then
self:__PickedUp( 0.1, PickupZone )
end
end
--- On after PickedUp event.
-- @param #AI_CARGO self
-- @param Wrapper.Group#GROUP Carrier
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided.
function AI_CARGO:onafterPickedUp( Carrier, From, Event, To, PickupZone )
self:F( { Carrier, From, Event, To } )
Carrier:RouteResume()
local HasCargo = false
if Carrier and Carrier:IsAlive() then
for Cargo, CarrierUnit in pairs( self.Carrier_Cargo ) do
HasCargo = true
break
end
end
self.Relocating = false
if HasCargo then
self:F( "Transporting" )
self.Transporting = true
end
end
--- On after Unload event.
-- @param #AI_CARGO self
-- @param Wrapper.Group#GROUP Carrier
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE.
function AI_CARGO:onafterUnload( Carrier, From, Event, To, DeployZone, Defend )
self:F( { Carrier, From, Event, To, DeployZone, Defend = Defend } )
local UnboardInterval = 5
local UnboardDelay = 5
if Carrier and Carrier:IsAlive() then
for _, CarrierUnit in pairs( Carrier:GetUnits() ) do
local CarrierUnit = CarrierUnit -- Wrapper.Unit#UNIT
Carrier:RouteStop()
for _, Cargo in pairs( CarrierUnit:GetCargo() ) do
self:F( { Cargo = Cargo:GetName(), Isloaded = Cargo:IsLoaded() } )
if Cargo:IsLoaded() then
Cargo:__UnBoard( UnboardDelay )
UnboardDelay = UnboardDelay + Cargo:GetCount() * UnboardInterval
self:__Unboard( UnboardDelay, Cargo, CarrierUnit, DeployZone, Defend )
if not Defend == true then
Cargo:SetDeployed( true )
end
end
end
end
end
end
--- On after Unboard event.
-- @param #AI_CARGO self
-- @param Wrapper.Group#GROUP Carrier
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param #string Cargo.Cargo#CARGO Cargo Cargo object.
-- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE.
function AI_CARGO:onafterUnboard( Carrier, From, Event, To, Cargo, CarrierUnit, DeployZone, Defend )
self:F( { Carrier, From, Event, To, Cargo:GetName(), DeployZone = DeployZone, Defend = Defend } )
if Carrier and Carrier:IsAlive() then
if not Cargo:IsUnLoaded() then
self:__Unboard( 10, Cargo, CarrierUnit, DeployZone, Defend )
return
end
end
self:Unloaded( Cargo, CarrierUnit, DeployZone, Defend )
end
--- On after Unloaded event.
-- @param #AI_CARGO self
-- @param Wrapper.Group#GROUP Carrier
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param #string Cargo.Cargo#CARGO Cargo Cargo object.
-- @param #boolean Deployed Cargo is deployed.
-- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE.
function AI_CARGO:onafterUnloaded( Carrier, From, Event, To, Cargo, CarrierUnit, DeployZone, Defend )
self:F( { Carrier, From, Event, To, Cargo:GetName(), DeployZone = DeployZone, Defend = Defend } )
local AllUnloaded = true
--Cargo:Regroup()
if Carrier and Carrier:IsAlive() then
for _, CarrierUnit in pairs( Carrier:GetUnits() ) do
local CarrierUnit = CarrierUnit -- Wrapper.Unit#UNIT
local IsEmpty = CarrierUnit:IsCargoEmpty()
self:T({ IsEmpty = IsEmpty })
if not IsEmpty then
AllUnloaded = false
break
end
end
if AllUnloaded == true then
if DeployZone == true then
self.Carrier_Cargo = {}
end
self.CargoCarrier = Carrier
end
end
if AllUnloaded == true then
self:__Deployed( 5, DeployZone, Defend )
end
end
--- On after Deployed event.
-- @param #AI_CARGO self
-- @param Wrapper.Group#GROUP Carrier
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE.
-- @param #boolean Defend Defend for APCs.
function AI_CARGO:onafterDeployed( Carrier, From, Event, To, DeployZone, Defend )
self:F( { Carrier, From, Event, To, DeployZone = DeployZone, Defend = Defend } )
if not Defend == true then
self.Transporting = false
else
self:F( "Defending" )
end
end

View File

@@ -1,607 +0,0 @@
--- **AI** - Models the intelligent transportation of cargo using ground vehicles.
--
-- ===
--
-- ### Author: **FlightControl**
--
-- ===
--
-- @module AI.AI_Cargo_APC
-- @image AI_Cargo_Dispatching_For_APC.JPG
-- @type AI_CARGO_APC
-- @extends AI.AI_Cargo#AI_CARGO
--- Brings a dynamic cargo handling capability for an AI vehicle group.
--
-- Armoured Personnel Carriers (APC), Trucks, Jeeps and other ground based carrier equipment can be mobilized to intelligently transport infantry and other cargo within the simulation.
--
-- The AI_CARGO_APC class uses the @{Cargo.Cargo} capabilities within the MOOSE framework.
-- @{Cargo.Cargo} must be declared within the mission to make the AI_CARGO_APC object recognize the cargo.
-- Please consult the @{Cargo.Cargo} module for more information.
--
-- ## Cargo loading.
--
-- The module will load automatically cargo when the APCs are within boarding or loading radius.
-- The boarding or loading radius is specified when the cargo is created in the simulation, and therefore, this radius depends on the type of cargo
-- and the specified boarding radius.
--
-- ## **Defending** the APCs when enemies nearby.
--
-- Cargo will defend the carrier with its available arms, and to avoid cargo being lost within the battlefield.
--
-- When the APCs are approaching enemy units, something special is happening.
-- The APCs will stop moving, and the loaded infantry will unboard and follow the APCs and will help to defend the group.
-- The carrier will hold the route once the unboarded infantry is further than 50 meters from the APCs,
-- to ensure that the APCs are not too far away from the following running infantry.
-- Once all enemies are cleared, the infantry will board again automatically into the APCs. Once boarded, the APCs will follow its pre-defined route.
--
-- A combat radius needs to be specified in meters at the @{#AI_CARGO_APC.New}() method.
-- This combat radius will trigger the unboarding of troops when enemies are within the combat radius around the APCs.
-- During my tests, I've noticed that there is a balance between ensuring that the infantry is within sufficient hit radius (effectiveness) versus
-- vulnerability of the infantry. It all depends on the kind of enemies that are expected to be encountered.
-- A combat radius of 350 meters to 500 meters has been proven to be the most effective and efficient.
--
-- However, when the defense of the carrier, is not required, it must be switched off.
-- This is done by disabling the defense of the carrier using the method @{#AI_CARGO_APC.SetCombatRadius}(), and providing a combat radius of 0 meters.
-- It can be switched on later when required by reenabling the defense using the method and providing a combat radius larger than 0.
--
-- ## Infantry or cargo **health**.
--
-- When infantry is unboarded from the APCs, the infantry is actually respawned into the battlefield.
-- As a result, the unboarding infantry is very _healthy_ every time it unboards.
-- This is due to the limitation of the DCS simulator, which is not able to specify the health of new spawned units as a parameter.
-- However, infantry that was destroyed when unboarded and following the APCs, won't be respawned again. Destroyed is destroyed.
-- As a result, there is some additional strength that is gained when an unboarding action happens, but in terms of simulation balance this has
-- marginal impact on the overall battlefield simulation. Fortunately, the firing strength of infantry is limited, and thus, respacing healthy infantry every
-- time is not so much of an issue ...
--
-- ## Control the APCs on the map.
--
-- It is possible also as a human ground commander to influence the path of the APCs, by pointing a new path using the DCS user interface on the map.
-- In this case, the APCs will change the direction towards its new indicated route. However, there is a catch!
-- Once the APCs are near the enemy, and infantry is unboarded, the APCs won't be able to hold the route until the infantry could catch up.
-- The APCs will simply drive on and won't stop! This is a limitation in ED that prevents user actions being controlled by the scripting engine.
-- No workaround is possible on this.
--
-- ## Cargo deployment.
--
-- Using the @{#AI_CARGO_APC.Deploy}() method, you are able to direct the APCs towards a point on the battlefield to unboard/unload the cargo at the specific coordinate.
-- The APCs will follow nearby roads as much as possible, to ensure fast and clean cargo transportation between the objects and villages in the simulation environment.
--
-- ## Cargo pickup.
--
-- Using the @{#AI_CARGO_APC.Pickup}() method, you are able to direct the APCs towards a point on the battlefield to board/load the cargo at the specific coordinate.
-- The APCs will follow nearby roads as much as possible, to ensure fast and clean cargo transportation between the objects and villages in the simulation environment.
--
--
-- # Developer Note
--
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
-- Therefore, this class is considered to be deprecated
--
--
-- @field #AI_CARGO_APC
AI_CARGO_APC = {
ClassName = "AI_CARGO_APC",
Coordinate = nil, -- Core.Point#COORDINATE,
}
--- Creates a new AI_CARGO_APC object.
-- @param #AI_CARGO_APC self
-- @param Wrapper.Group#GROUP APC The carrier APC group.
-- @param Core.Set#SET_CARGO CargoSet The set of cargo to be transported.
-- @param #number CombatRadius Provide the combat radius to defend the carrier by unboarding the cargo when enemies are nearby. When the combat radius is 0, no defense will happen of the carrier.
-- @return #AI_CARGO_APC
function AI_CARGO_APC:New( APC, CargoSet, CombatRadius )
local self = BASE:Inherit( self, AI_CARGO:New( APC, CargoSet ) ) -- #AI_CARGO_APC
self:AddTransition( "*", "Monitor", "*" )
self:AddTransition( "*", "Follow", "Following" )
self:AddTransition( "*", "Guard", "Unloaded" )
self:AddTransition( "*", "Home", "*" )
self:AddTransition( "*", "Reload", "Boarding" )
self:AddTransition( "*", "Deployed", "*" )
self:AddTransition( "*", "PickedUp", "*" )
self:AddTransition( "*", "Destroyed", "Destroyed" )
self:SetCombatRadius( CombatRadius )
self:SetCarrier( APC )
return self
end
--- Set the Carrier.
-- @param #AI_CARGO_APC self
-- @param Wrapper.Group#GROUP CargoCarrier
-- @return #AI_CARGO_APC
function AI_CARGO_APC:SetCarrier( CargoCarrier )
self.CargoCarrier = CargoCarrier -- Wrapper.Group#GROUP
self.CargoCarrier:SetState( self.CargoCarrier, "AI_CARGO_APC", self )
CargoCarrier:HandleEvent( EVENTS.Dead )
function CargoCarrier:OnEventDead( EventData )
self:F({"dead"})
local AICargoTroops = self:GetState( self, "AI_CARGO_APC" )
self:F({AICargoTroops=AICargoTroops})
if AICargoTroops then
self:F({})
if not AICargoTroops:Is( "Loaded" ) then
-- There are enemies within combat radius. Unload the CargoCarrier.
AICargoTroops:Destroyed()
end
end
end
-- CargoCarrier:HandleEvent( EVENTS.Hit )
--
-- function CargoCarrier:OnEventHit( EventData )
-- self:F({"hit"})
-- local AICargoTroops = self:GetState( self, "AI_CARGO_APC" )
-- if AICargoTroops then
-- self:F( { OnHitLoaded = AICargoTroops:Is( "Loaded" ) } )
-- if AICargoTroops:Is( "Loaded" ) or AICargoTroops:Is( "Boarding" ) then
-- -- There are enemies within combat radius. Unload the CargoCarrier.
-- AICargoTroops:Unload( false )
-- end
-- end
-- end
self.Zone = ZONE_UNIT:New( self.CargoCarrier:GetName() .. "-Zone", self.CargoCarrier, self.CombatRadius )
self.Coalition = self.CargoCarrier:GetCoalition()
self:SetControllable( CargoCarrier )
self:Guard()
return self
end
--- Set whether or not the carrier will use roads to *pickup* and *deploy* the cargo.
-- @param #AI_CARGO_APC self
-- @param #boolean Offroad If true, carrier will not use roads. If `nil` or `false` the carrier will use roads when available.
-- @param #number Formation Offroad formation used. Default is `ENUMS.Formation.Vehicle.Offroad`.
-- @return #AI_CARGO_APC self
function AI_CARGO_APC:SetOffRoad(Offroad, Formation)
self:SetPickupOffRoad(Offroad, Formation)
self:SetDeployOffRoad(Offroad, Formation)
return self
end
--- Set whether the carrier will *not* use roads to *pickup* the cargo.
-- @param #AI_CARGO_APC self
-- @param #boolean Offroad If true, carrier will not use roads.
-- @param #number Formation Offroad formation used. Default is `ENUMS.Formation.Vehicle.Offroad`.
-- @return #AI_CARGO_APC self
function AI_CARGO_APC:SetPickupOffRoad(Offroad, Formation)
self.pickupOffroad=Offroad
self.pickupFormation=Formation or ENUMS.Formation.Vehicle.OffRoad
return self
end
--- Set whether the carrier will *not* use roads to *deploy* the cargo.
-- @param #AI_CARGO_APC self
-- @param #boolean Offroad If true, carrier will not use roads.
-- @param #number Formation Offroad formation used. Default is `ENUMS.Formation.Vehicle.Offroad`.
-- @return #AI_CARGO_APC self
function AI_CARGO_APC:SetDeployOffRoad(Offroad, Formation)
self.deployOffroad=Offroad
self.deployFormation=Formation or ENUMS.Formation.Vehicle.OffRoad
return self
end
--- Find a free Carrier within a radius.
-- @param #AI_CARGO_APC self
-- @param Core.Point#COORDINATE Coordinate
-- @param #number Radius
-- @return Wrapper.Group#GROUP NewCarrier
function AI_CARGO_APC:FindCarrier( Coordinate, Radius )
local CoordinateZone = ZONE_RADIUS:New( "Zone" , Coordinate:GetVec2(), Radius )
CoordinateZone:Scan( { Object.Category.UNIT } )
for _, DCSUnit in pairs( CoordinateZone:GetScannedUnits() ) do
local NearUnit = UNIT:Find( DCSUnit )
self:F({NearUnit=NearUnit})
if not NearUnit:GetState( NearUnit, "AI_CARGO_APC" ) then
local Attributes = NearUnit:GetDesc()
self:F({Desc=Attributes})
if NearUnit:HasAttribute( "Trucks" ) then
return NearUnit:GetGroup()
end
end
end
return nil
end
--- Enable/Disable unboarding of cargo (infantry) when enemies are nearby (to help defend the carrier).
-- This is only valid for APCs and trucks etc, thus ground vehicles.
-- @param #AI_CARGO_APC self
-- @param #number CombatRadius Provide the combat radius to defend the carrier by unboarding the cargo when enemies are nearby.
-- When the combat radius is 0, no defense will happen of the carrier.
-- When the combat radius is not provided, no defense will happen!
-- @return #AI_CARGO_APC
-- @usage
--
-- -- Disembark the infantry when the carrier is under attack.
-- AICargoAPC:SetCombatRadius( true )
--
-- -- Keep the cargo in the carrier when the carrier is under attack.
-- AICargoAPC:SetCombatRadius( false )
function AI_CARGO_APC:SetCombatRadius( CombatRadius )
self.CombatRadius = CombatRadius or 0
if self.CombatRadius > 0 then
self:__Monitor( -5 )
end
return self
end
--- Follow Infantry to the Carrier.
-- @param #AI_CARGO_APC self
-- @param #AI_CARGO_APC Me
-- @param Wrapper.Unit#UNIT APCUnit
-- @param Cargo.CargoGroup#CARGO_GROUP Cargo
-- @return #AI_CARGO_APC
function AI_CARGO_APC:FollowToCarrier( Me, APCUnit, CargoGroup )
local InfantryGroup = CargoGroup:GetGroup()
self:F( { self = self:GetClassNameAndID(), InfantryGroup = InfantryGroup:GetName() } )
--if self:Is( "Following" ) then
if APCUnit:IsAlive() then
-- We check if the Cargo is near to the CargoCarrier.
if InfantryGroup:IsPartlyInZone( ZONE_UNIT:New( "Radius", APCUnit, 25 ) ) then
-- The Cargo does not need to follow the Carrier.
Me:Guard()
else
self:F( { InfantryGroup = InfantryGroup:GetName() } )
if InfantryGroup:IsAlive() then
self:F( { InfantryGroup = InfantryGroup:GetName() } )
local Waypoints = {}
-- Calculate the new Route.
local FromCoord = InfantryGroup:GetCoordinate()
local FromGround = FromCoord:WaypointGround( 10, "Diamond" )
self:F({FromGround=FromGround})
table.insert( Waypoints, FromGround )
local ToCoord = APCUnit:GetCoordinate():GetRandomCoordinateInRadius( 10, 5 )
local ToGround = ToCoord:WaypointGround( 10, "Diamond" )
self:F({ToGround=ToGround})
table.insert( Waypoints, ToGround )
local TaskRoute = InfantryGroup:TaskFunction( "AI_CARGO_APC.FollowToCarrier", Me, APCUnit, CargoGroup )
self:F({Waypoints = Waypoints})
local Waypoint = Waypoints[#Waypoints]
InfantryGroup:SetTaskWaypoint( Waypoint, TaskRoute ) -- Set for the given Route at Waypoint 2 the TaskRouteToZone.
InfantryGroup:Route( Waypoints, 1 ) -- Move after a random seconds to the Route. See the Route method for details.
end
end
end
end
--- On after Monitor event.
-- @param #AI_CARGO_APC self
-- @param Wrapper.Group#GROUP APC
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
function AI_CARGO_APC:onafterMonitor( APC, From, Event, To )
self:F( { APC, From, Event, To, IsTransporting = self:IsTransporting() } )
if self.CombatRadius > 0 then
if APC and APC:IsAlive() then
if self.CarrierCoordinate then
if self:IsTransporting() == true then
local Coordinate = APC:GetCoordinate()
if self:Is( "Unloaded" ) or self:Is( "Loaded" ) then
self.Zone:Scan( { Object.Category.UNIT } )
if self.Zone:IsAllInZoneOfCoalition( self.Coalition ) then
if self:Is( "Unloaded" ) then
-- There are no enemies within combat radius. Reload the CargoCarrier.
self:Reload()
end
else
if self:Is( "Loaded" ) then
-- There are enemies within combat radius. Unload the CargoCarrier.
self:__Unload( 1, nil, true ) -- The 2nd parameter is true, which means that the unload is for defending the carrier, not to deploy!
else
if self:Is( "Unloaded" ) then
--self:Follow()
end
self:F( "I am here" .. self:GetCurrentState() )
if self:Is( "Following" ) then
for Cargo, APCUnit in pairs( self.Carrier_Cargo ) do
local Cargo = Cargo -- Cargo.Cargo#CARGO
local APCUnit = APCUnit -- Wrapper.Unit#UNIT
if Cargo:IsAlive() then
if not Cargo:IsNear( APCUnit, 40 ) then
APCUnit:RouteStop()
self.CarrierStopped = true
else
if self.CarrierStopped then
if Cargo:IsNear( APCUnit, 25 ) then
APCUnit:RouteResume()
self.CarrierStopped = nil
end
end
end
end
end
end
end
end
end
end
end
self.CarrierCoordinate = APC:GetCoordinate()
end
self:__Monitor( -5 )
end
end
--- On after Follow event.
-- @param #AI_CARGO_APC self
-- @param Wrapper.Group#GROUP APC
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
function AI_CARGO_APC:onafterFollow( APC, From, Event, To )
self:F( { APC, From, Event, To } )
self:F( "Follow" )
if APC and APC:IsAlive() then
for Cargo, APCUnit in pairs( self.Carrier_Cargo ) do
local Cargo = Cargo -- Cargo.Cargo#CARGO
if Cargo:IsUnLoaded() then
self:FollowToCarrier( self, APCUnit, Cargo )
APCUnit:RouteResume()
end
end
end
end
--- Pickup task function. Triggers Load event.
-- @param Wrapper.Group#GROUP APC The cargo carrier group.
-- @param #AI_CARGO_APC sel `AI_CARGO_APC` class.
-- @param Core.Point#COORDINATE Coordinate. The coordinate (not used).
-- @param #number Speed Speed (not used).
-- @param Core.Zone#ZONE PickupZone Pickup zone.
function AI_CARGO_APC._Pickup(APC, self, Coordinate, Speed, PickupZone)
APC:F( { "AI_CARGO_APC._Pickup:", APC:GetName() } )
if APC:IsAlive() then
self:Load( PickupZone )
end
end
--- Deploy task function. Triggers Unload event.
-- @param Wrapper.Group#GROUP APC The cargo carrier group.
-- @param #AI_CARGO_APC self `AI_CARGO_APC` class.
-- @param Core.Point#COORDINATE Coordinate. The coordinate (not used).
-- @param Core.Zone#ZONE DeployZone Deploy zone.
function AI_CARGO_APC._Deploy(APC, self, Coordinate, DeployZone)
APC:F( { "AI_CARGO_APC._Deploy:", APC } )
if APC:IsAlive() then
self:Unload( DeployZone )
end
end
--- On after Pickup event.
-- @param #AI_CARGO_APC self
-- @param Wrapper.Group#GROUP APC
-- @param From
-- @param Event
-- @param To
-- @param Core.Point#COORDINATE Coordinate of the pickup point.
-- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go.
-- @param #number Height Height in meters to move to the pickup coordinate. This parameter is ignored for APCs.
-- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided.
function AI_CARGO_APC:onafterPickup( APC, From, Event, To, Coordinate, Speed, Height, PickupZone )
if APC and APC:IsAlive() then
if Coordinate then
self.RoutePickup = true
local _speed=Speed or APC:GetSpeedMax()*0.5
-- Route on road.
local Waypoints = {}
if self.pickupOffroad then
Waypoints[1]=APC:GetCoordinate():WaypointGround(Speed, self.pickupFormation)
Waypoints[2]=Coordinate:WaypointGround(_speed, self.pickupFormation, DCSTasks)
else
Waypoints=APC:TaskGroundOnRoad(Coordinate, _speed, ENUMS.Formation.Vehicle.OffRoad, true)
end
local TaskFunction = APC:TaskFunction( "AI_CARGO_APC._Pickup", self, Coordinate, Speed, PickupZone )
local Waypoint = Waypoints[#Waypoints]
APC:SetTaskWaypoint( Waypoint, TaskFunction ) -- Set for the given Route at Waypoint 2 the TaskRouteToZone.
APC:Route( Waypoints, 1 ) -- Move after a random seconds to the Route. See the Route method for details.
else
AI_CARGO_APC._Pickup( APC, self, Coordinate, Speed, PickupZone )
end
self:GetParent( self, AI_CARGO_APC ).onafterPickup( self, APC, From, Event, To, Coordinate, Speed, Height, PickupZone )
end
end
--- On after Deploy event.
-- @param #AI_CARGO_APC self
-- @param Wrapper.Group#GROUP APC
-- @param From
-- @param Event
-- @param To
-- @param Core.Point#COORDINATE Coordinate Deploy place.
-- @param #number Speed Speed in km/h to drive to the depoly coordinate. Default is 50% of max possible speed the unit can go.
-- @param #number Height Height in meters to move to the deploy coordinate. This parameter is ignored for APCs.
-- @param Core.Zone#ZONE DeployZone The zone where the cargo will be deployed.
function AI_CARGO_APC:onafterDeploy( APC, From, Event, To, Coordinate, Speed, Height, DeployZone )
if APC and APC:IsAlive() then
self.RouteDeploy = true
-- Set speed in km/h.
local speedmax=APC:GetSpeedMax()
local _speed=Speed or speedmax*0.5
_speed=math.min(_speed, speedmax)
-- Route on road.
local Waypoints = {}
if self.deployOffroad then
Waypoints[1]=APC:GetCoordinate():WaypointGround(Speed, self.deployFormation)
Waypoints[2]=Coordinate:WaypointGround(_speed, self.deployFormation, DCSTasks)
else
Waypoints=APC:TaskGroundOnRoad(Coordinate, _speed, ENUMS.Formation.Vehicle.OffRoad, true)
end
-- Task function
local TaskFunction = APC:TaskFunction( "AI_CARGO_APC._Deploy", self, Coordinate, DeployZone )
-- Last waypoint
local Waypoint = Waypoints[#Waypoints]
-- Set task function
APC:SetTaskWaypoint(Waypoint, TaskFunction) -- Set for the given Route at Waypoint 2 the TaskRouteToZone.
-- Route group
APC:Route( Waypoints, 1 ) -- Move after a random seconds to the Route. See the Route method for details.
-- Call parent function.
self:GetParent( self, AI_CARGO_APC ).onafterDeploy( self, APC, From, Event, To, Coordinate, Speed, Height, DeployZone )
end
end
--- On after Unloaded event.
-- @param #AI_CARGO_APC self
-- @param Wrapper.Group#GROUP Carrier
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param #string Cargo.Cargo#CARGO Cargo Cargo object.
-- @param #boolean Deployed Cargo is deployed.
-- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE.
function AI_CARGO_APC:onafterUnloaded( Carrier, From, Event, To, Cargo, CarrierUnit, DeployZone, Defend )
self:F( { Carrier, From, Event, To, DeployZone = DeployZone, Defend = Defend } )
self:GetParent( self, AI_CARGO_APC ).onafterUnloaded( self, Carrier, From, Event, To, Cargo, CarrierUnit, DeployZone, Defend )
-- If Defend == true then we need to scan for possible enemies within combat zone and engage only ground forces.
if Defend == true then
self.Zone:Scan( { Object.Category.UNIT } )
if not self.Zone:IsAllInZoneOfCoalition( self.Coalition ) then
-- OK, enemies nearby, now find the enemies and attack them.
local AttackUnits = self.Zone:GetScannedUnits() -- #list<DCS#Unit>
local Move = {}
local CargoGroup = Cargo.CargoObject -- Wrapper.Group#GROUP
Move[#Move+1] = CargoGroup:GetCoordinate():WaypointGround( 70, "Custom" )
for UnitId, AttackUnit in pairs( AttackUnits ) do
local MooseUnit = UNIT:Find( AttackUnit )
if MooseUnit:GetCoalition() ~= CargoGroup:GetCoalition() then
Move[#Move+1] = MooseUnit:GetCoordinate():WaypointGround( 70, "Line abreast" )
--MoveTo.Task = CargoGroup:TaskCombo( CargoGroup:TaskAttackUnit( MooseUnit, true ) )
self:F( { MooseUnit = MooseUnit:GetName(), CargoGroup = CargoGroup:GetName() } )
end
end
CargoGroup:RoutePush( Move, 0.1 )
end
end
end
--- On after Deployed event.
-- @param #AI_CARGO_APC self
-- @param Wrapper.Group#GROUP Carrier
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE.
function AI_CARGO_APC:onafterDeployed( APC, From, Event, To, DeployZone, Defend )
self:F( { APC, From, Event, To, DeployZone = DeployZone, Defend = Defend } )
self:__Guard( 0.1 )
self:GetParent( self, AI_CARGO_APC ).onafterDeployed( self, APC, From, Event, To, DeployZone, Defend )
end
--- On after Home event.
-- @param #AI_CARGO_APC self
-- @param Wrapper.Group#GROUP APC
-- @param From
-- @param Event
-- @param To
-- @param Core.Point#COORDINATE Coordinate Home place.
-- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go.
-- @param #number Height Height in meters to move to the home coordinate. This parameter is ignored for APCs.
function AI_CARGO_APC:onafterHome( APC, From, Event, To, Coordinate, Speed, Height, HomeZone )
if APC and APC:IsAlive() ~= nil then
self.RouteHome = true
Speed = Speed or APC:GetSpeedMax()*0.5
local Waypoints = APC:TaskGroundOnRoad( Coordinate, Speed, "Line abreast", true )
self:F({Waypoints = Waypoints})
local Waypoint = Waypoints[#Waypoints]
APC:Route( Waypoints, 1 ) -- Move after a random seconds to the Route. See the Route method for details.
end
end

View File

@@ -1,510 +0,0 @@
--- **AI** - Models the intelligent transportation of cargo using airplanes.
--
-- ===
--
-- ### Author: **FlightControl**
--
-- ===
--
-- @module AI.AI_Cargo_Airplane
-- @image AI_Cargo_Dispatching_For_Airplanes.JPG
-- @type AI_CARGO_AIRPLANE
-- @extends Core.Fsm#FSM_CONTROLLABLE
--- Brings a dynamic cargo handling capability for an AI airplane group.
--
-- Airplane carrier equipment can be mobilized to intelligently transport infantry and other cargo within the simulation between airbases.
--
-- The AI_CARGO_AIRPLANE module uses the @{Cargo.Cargo} capabilities within the MOOSE framework.
-- @{Cargo.Cargo} must be declared within the mission to make AI_CARGO_AIRPLANE recognize the cargo.
-- Please consult the @{Cargo.Cargo} module for more information.
--
-- ## Cargo pickup.
--
-- Using the @{#AI_CARGO_AIRPLANE.Pickup}() method, you are able to direct the helicopters towards a point on the battlefield to board/load the cargo at the specific coordinate.
-- Ensure that the landing zone is horizontally flat, and that trees cannot be found in the landing vicinity, or the helicopters won't land or will even crash!
--
-- ## Cargo deployment.
--
-- Using the @{#AI_CARGO_AIRPLANE.Deploy}() method, you are able to direct the helicopters towards a point on the battlefield to unboard/unload the cargo at the specific coordinate.
-- Ensure that the landing zone is horizontally flat, and that trees cannot be found in the landing vicinity, or the helicopters won't land or will even crash!
--
-- ## Infantry health.
--
-- When infantry is unboarded from the APCs, the infantry is actually respawned into the battlefield.
-- As a result, the unboarding infantry is very _healthy_ every time it unboards.
-- This is due to the limitation of the DCS simulator, which is not able to specify the health of new spawned units as a parameter.
-- However, infantry that was destroyed when unboarded, won't be respawned again. Destroyed is destroyed.
-- As a result, there is some additional strength that is gained when an unboarding action happens, but in terms of simulation balance this has
-- marginal impact on the overall battlefield simulation. Fortunately, the firing strength of infantry is limited, and thus, respacing healthy infantry every
-- time is not so much of an issue ...
--
--
-- # Developer Note
--
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
-- Therefore, this class is considered to be deprecated
--
-- @field #AI_CARGO_AIRPLANE
AI_CARGO_AIRPLANE = {
ClassName = "AI_CARGO_AIRPLANE",
Coordinate = nil, -- Core.Point#COORDINATE
}
--- Creates a new AI_CARGO_AIRPLANE object.
-- @param #AI_CARGO_AIRPLANE self
-- @param Wrapper.Group#GROUP Airplane Plane used for transportation of cargo.
-- @param Core.Set#SET_CARGO CargoSet Cargo set to be transported.
-- @return #AI_CARGO_AIRPLANE
function AI_CARGO_AIRPLANE:New( Airplane, CargoSet )
local self = BASE:Inherit( self, AI_CARGO:New( Airplane, CargoSet ) ) -- #AI_CARGO_AIRPLANE
self:AddTransition( "*", "Landed", "*" )
self:AddTransition( "*", "Home" , "*" )
self:AddTransition( "*", "Destroyed", "Destroyed" )
--- Pickup Handler OnBefore for AI_CARGO_AIRPLANE
-- @function [parent=#AI_CARGO_AIRPLANE] OnBeforePickup
-- @param #AI_CARGO_AIRPLANE self
-- @param Wrapper.Group#GROUP Airplane Cargo transport plane.
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Wrapper.Airbase#AIRBASE Airbase Airbase where troops are picked up.
-- @param #number Speed in km/h for travelling to pickup base.
-- @return #boolean
--- Pickup Handler OnAfter for AI_CARGO_AIRPLANE
-- @function [parent=#AI_CARGO_AIRPLANE] OnAfterPickup
-- @param #AI_CARGO_AIRPLANE self
-- @param Wrapper.Group#GROUP Airplane Cargo transport plane.
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Core.Point#COORDINATE Coordinate The coordinate where to pickup stuff.
-- @param #number Speed Speed in km/h for travelling to pickup base.
-- @param #number Height Height in meters to move to the pickup coordinate.
-- @param Core.Zone#ZONE_AIRBASE PickupZone The airbase zone where the cargo will be picked up.
--- Pickup Trigger for AI_CARGO_AIRPLANE
-- @function [parent=#AI_CARGO_AIRPLANE] Pickup
-- @param #AI_CARGO_AIRPLANE self
-- @param Core.Point#COORDINATE Coordinate The coordinate where to pickup stuff.
-- @param #number Speed Speed in km/h for travelling to pickup base.
-- @param #number Height Height in meters to move to the pickup coordinate.
-- @param Core.Zone#ZONE_AIRBASE PickupZone The airbase zone where the cargo will be picked up.
--- Pickup Asynchronous Trigger for AI_CARGO_AIRPLANE
-- @function [parent=#AI_CARGO_AIRPLANE] __Pickup
-- @param #AI_CARGO_AIRPLANE self
-- @param #number Delay Delay in seconds.
-- @param Core.Point#COORDINATE Coordinate The coordinate where to pickup stuff.
-- @param #number Speed Speed in km/h for travelling to pickup base.
-- @param #number Height Height in meters to move to the pickup coordinate.
-- @param Core.Zone#ZONE_AIRBASE PickupZone The airbase zone where the cargo will be picked up.
--- Deploy Handler OnBefore for AI_CARGO_AIRPLANE
-- @function [parent=#AI_CARGO_AIRPLANE] OnBeforeDeploy
-- @param #AI_CARGO_AIRPLANE self
-- @param Wrapper.Group#GROUP Airplane Cargo plane.
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param Wrapper.Airbase#AIRBASE Airbase Destination airbase where troops are deployed.
-- @param #number Speed Speed in km/h for travelling to deploy base.
-- @return #boolean
--- Deploy Handler OnAfter for AI_CARGO_AIRPLANE
-- @function [parent=#AI_CARGO_AIRPLANE] OnAfterDeploy
-- @param #AI_CARGO_AIRPLANE self
-- @param Wrapper.Group#GROUP Airplane Cargo plane.
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Core.Point#COORDINATE Coordinate Coordinate where to deploy stuff.
-- @param #number Speed Speed in km/h for travelling to the deploy base.
-- @param #number Height Height in meters to move to the home coordinate.
-- @param Core.Zone#ZONE_AIRBASE DeployZone The airbase zone where the cargo will be deployed.
--- Deploy Trigger for AI_CARGO_AIRPLANE
-- @function [parent=#AI_CARGO_AIRPLANE] Deploy
-- @param #AI_CARGO_AIRPLANE self
-- @param Core.Point#COORDINATE Coordinate Coordinate where to deploy stuff.
-- @param #number Speed Speed in km/h for travelling to the deploy base.
-- @param #number Height Height in meters to move to the home coordinate.
-- @param Core.Zone#ZONE_AIRBASE DeployZone The airbase zone where the cargo will be deployed.
--- Deploy Asynchronous Trigger for AI_CARGO_AIRPLANE
-- @function [parent=#AI_CARGO_AIRPLANE] __Deploy
-- @param #AI_CARGO_AIRPLANE self
-- @param #number Delay Delay in seconds.
-- @param Core.Point#COORDINATE Coordinate Coordinate where to deploy stuff.
-- @param #number Speed Speed in km/h for travelling to the deploy base.
-- @param #number Height Height in meters to move to the home coordinate.
-- @param Core.Zone#ZONE_AIRBASE DeployZone The airbase zone where the cargo will be deployed.
--- On after Loaded event, i.e. triggered when the cargo is inside the carrier.
-- @function [parent=#AI_CARGO_AIRPLANE] OnAfterLoaded
-- @param #AI_CARGO_AIRPLANE self
-- @param Wrapper.Group#GROUP Airplane Cargo plane.
-- @param From
-- @param Event
-- @param To
--- On after Deployed event.
-- @function [parent=#AI_CARGO_AIRPLANE] OnAfterDeployed
-- @param #AI_CARGO_AIRPLANE self
-- @param Wrapper.Group#GROUP Airplane Cargo plane.
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed.
-- Set carrier.
self:SetCarrier( Airplane )
return self
end
--- Set the Carrier (controllable). Also initializes events for carrier and defines the coalition.
-- @param #AI_CARGO_AIRPLANE self
-- @param Wrapper.Group#GROUP Airplane Transport plane.
-- @return #AI_CARGO_AIRPLANE self
function AI_CARGO_AIRPLANE:SetCarrier( Airplane )
local AICargo = self
self.Airplane = Airplane -- Wrapper.Group#GROUP
self.Airplane:SetState( self.Airplane, "AI_CARGO_AIRPLANE", self )
self.RoutePickup = false
self.RouteDeploy = false
Airplane:HandleEvent( EVENTS.Dead )
Airplane:HandleEvent( EVENTS.Hit )
Airplane:HandleEvent( EVENTS.EngineShutdown )
function Airplane:OnEventDead( EventData )
local AICargoTroops = self:GetState( self, "AI_CARGO_AIRPLANE" )
self:F({AICargoTroops=AICargoTroops})
if AICargoTroops then
self:F({})
if not AICargoTroops:Is( "Loaded" ) then
-- There are enemies within combat range. Unload the Airplane.
AICargoTroops:Destroyed()
end
end
end
function Airplane:OnEventHit( EventData )
local AICargoTroops = self:GetState( self, "AI_CARGO_AIRPLANE" )
if AICargoTroops then
self:F( { OnHitLoaded = AICargoTroops:Is( "Loaded" ) } )
if AICargoTroops:Is( "Loaded" ) or AICargoTroops:Is( "Boarding" ) then
-- There are enemies within combat range. Unload the Airplane.
AICargoTroops:Unload()
end
end
end
function Airplane:OnEventEngineShutdown( EventData )
AICargo.Relocating = false
AICargo:Landed( self.Airplane )
end
self.Coalition = self.Airplane:GetCoalition()
self:SetControllable( Airplane )
return self
end
--- Find a free Carrier within a range.
-- @param #AI_CARGO_AIRPLANE self
-- @param Wrapper.Airbase#AIRBASE Airbase
-- @param #number Radius
-- @return Wrapper.Group#GROUP NewCarrier
function AI_CARGO_AIRPLANE:FindCarrier( Coordinate, Radius )
local CoordinateZone = ZONE_RADIUS:New( "Zone" , Coordinate:GetVec2(), Radius )
CoordinateZone:Scan( { Object.Category.UNIT } )
for _, DCSUnit in pairs( CoordinateZone:GetScannedUnits() ) do
local NearUnit = UNIT:Find( DCSUnit )
self:F({NearUnit=NearUnit})
if not NearUnit:GetState( NearUnit, "AI_CARGO_AIRPLANE" ) then
local Attributes = NearUnit:GetDesc()
self:F({Desc=Attributes})
if NearUnit:HasAttribute( "Trucks" ) then
self:SetCarrier( NearUnit )
break
end
end
end
end
--- On after "Landed" event. Called on engine shutdown and initiates the pickup mission or unloading event.
-- @param #AI_CARGO_AIRPLANE self
-- @param Wrapper.Group#GROUP Airplane Cargo transport plane.
-- @param From
-- @param Event
-- @param To
function AI_CARGO_AIRPLANE:onafterLanded( Airplane, From, Event, To )
self:F({Airplane, From, Event, To})
if Airplane and Airplane:IsAlive()~=nil then
-- Aircraft was sent to this airbase to pickup troops. Initiate loadling.
if self.RoutePickup == true then
self:Load( self.PickupZone )
end
-- Aircraft was send to this airbase to deploy troops. Initiate unloading.
if self.RouteDeploy == true then
self:Unload()
self.RouteDeploy = false
end
end
end
--- On after "Pickup" event. Routes transport to pickup airbase.
-- @param #AI_CARGO_AIRPLANE self
-- @param Wrapper.Group#GROUP Airplane Cargo transport plane.
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Core.Point#COORDINATE Coordinate The coordinate where to pickup stuff.
-- @param #number Speed Speed in km/h for travelling to pickup base.
-- @param #number Height Height in meters to move to the pickup coordinate.
-- @param Core.Zone#ZONE_AIRBASE PickupZone The airbase zone where the cargo will be picked up.
function AI_CARGO_AIRPLANE:onafterPickup( Airplane, From, Event, To, Coordinate, Speed, Height, PickupZone )
if Airplane and Airplane:IsAlive() then
local airbasepickup=Coordinate:GetClosestAirbase()
self.PickupZone = PickupZone or ZONE_AIRBASE:New(airbasepickup:GetName())
-- Get closest airbase of current position.
local ClosestAirbase, DistToAirbase=Airplane:GetCoordinate():GetClosestAirbase()
-- Two cases. Aircraft spawned in air or at an airbase.
if Airplane:InAir() then
self.Airbase=nil --> route will start in air
else
self.Airbase=ClosestAirbase
end
-- Set pickup airbase.
local Airbase = self.PickupZone:GetAirbase()
-- Distance from closest to pickup airbase ==> we need to know if we are already at the pickup airbase.
local Dist = Airbase:GetCoordinate():Get2DDistance(ClosestAirbase:GetCoordinate())
if Airplane:InAir() or Dist>500 then
-- Route aircraft to pickup airbase.
self:Route( Airplane, Airbase, Speed, Height )
-- Set airbase as starting point in the next Route() call.
self.Airbase = Airbase
-- Aircraft is on a pickup mission.
self.RoutePickup = true
else
-- We are already at the right airbase ==> Landed ==> triggers loading of troops. Is usually called at engine shutdown event.
self.RoutePickup=true
self:Landed()
end
self:GetParent( self, AI_CARGO_AIRPLANE ).onafterPickup( self, Airplane, From, Event, To, Coordinate, Speed, Height, self.PickupZone )
end
end
--- On after Depoly event. Routes plane to the airbase where the troops are deployed.
-- @param #AI_CARGO_AIRPLANE self
-- @param Wrapper.Group#GROUP Airplane Cargo transport plane.
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Core.Point#COORDINATE Coordinate Coordinate where to deploy stuff.
-- @param #number Speed Speed in km/h for travelling to the deploy base.
-- @param #number Height Height in meters to move to the home coordinate.
-- @param Core.Zone#ZONE_AIRBASE DeployZone The airbase zone where the cargo will be deployed.
function AI_CARGO_AIRPLANE:onafterDeploy( Airplane, From, Event, To, Coordinate, Speed, Height, DeployZone )
if Airplane and Airplane:IsAlive()~=nil then
local Airbase = Coordinate:GetClosestAirbase()
if DeployZone then
Airbase=DeployZone:GetAirbase()
end
-- Activate uncontrolled airplane.
if Airplane:IsAlive()==false then
Airplane:SetCommand({id = 'Start', params = {}})
end
-- Route to destination airbase.
self:Route( Airplane, Airbase, Speed, Height )
-- Aircraft is on a depoly mission.
self.RouteDeploy = true
-- Set destination airbase for next :Route() command.
self.Airbase = Airbase
self:GetParent( self, AI_CARGO_AIRPLANE ).onafterDeploy( self, Airplane, From, Event, To, Coordinate, Speed, Height, DeployZone )
end
end
--- On after Unload event. Cargo is beeing unloaded, i.e. the unboarding process is started.
-- @param #AI_CARGO_AIRPLANE self
-- @param Wrapper.Group#GROUP Airplane Cargo transport plane.
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Core.Zone#ZONE_AIRBASE DeployZone The airbase zone where the cargo will be deployed.
function AI_CARGO_AIRPLANE:onafterUnload( Airplane, From, Event, To, DeployZone )
local UnboardInterval = 10
local UnboardDelay = 10
if Airplane and Airplane:IsAlive() then
for _, AirplaneUnit in pairs( Airplane:GetUnits() ) do
local Cargos = AirplaneUnit:GetCargo()
for CargoID, Cargo in pairs( Cargos ) do
local Angle = 180
local CargoCarrierHeading = Airplane:GetHeading() -- Get Heading of object in degrees.
local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle )
self:T( { CargoCarrierHeading, CargoDeployHeading } )
local CargoDeployCoordinate = Airplane:GetPointVec2():Translate( 150, CargoDeployHeading )
Cargo:__UnBoard( UnboardDelay, CargoDeployCoordinate )
UnboardDelay = UnboardDelay + UnboardInterval
Cargo:SetDeployed( true )
self:__Unboard( UnboardDelay, Cargo, AirplaneUnit, DeployZone )
end
end
end
end
--- Route the airplane from one airport or it's current position to another airbase.
-- @param #AI_CARGO_AIRPLANE self
-- @param Wrapper.Group#GROUP Airplane Airplane group to be routed.
-- @param Wrapper.Airbase#AIRBASE Airbase Destination airbase.
-- @param #number Speed Speed in km/h. Default is 80% of max possible speed the group can do.
-- @param #number Height Height in meters to move to the Airbase.
-- @param #boolean Uncontrolled If true, spawn group in uncontrolled state.
function AI_CARGO_AIRPLANE:Route( Airplane, Airbase, Speed, Height, Uncontrolled )
if Airplane and Airplane:IsAlive() then
-- Set takeoff type.
local Takeoff = SPAWN.Takeoff.Cold
-- Get template of group.
local Template = Airplane:GetTemplate()
-- Nil check
if Template==nil then
return
end
-- Waypoints of the route.
local Points={}
-- To point.
local AirbasePointVec2 = Airbase:GetPointVec2()
local ToWaypoint = AirbasePointVec2:WaypointAir(POINT_VEC3.RoutePointAltType.BARO, "Land", "Landing", Speed or Airplane:GetSpeedMax()*0.8, true, Airbase)
--ToWaypoint["airdromeId"] = Airbase:GetID()
--ToWaypoint["speed_locked"] = true
-- If self.Airbase~=nil then group is currently at an airbase, where it should be respawned.
if self.Airbase then
-- Second point of the route. First point is done in RespawnAtCurrentAirbase() routine.
Template.route.points[2] = ToWaypoint
-- Respawn group at the current airbase.
Airplane:RespawnAtCurrentAirbase(Template, Takeoff, Uncontrolled)
else
-- From point.
local GroupPoint = Airplane:GetVec2()
local FromWaypoint = {}
FromWaypoint.x = GroupPoint.x
FromWaypoint.y = GroupPoint.y
FromWaypoint.type = "Turning Point"
FromWaypoint.action = "Turning Point"
FromWaypoint.speed = Airplane:GetSpeedMax()*0.8
-- The two route points.
Points[1] = FromWaypoint
Points[2] = ToWaypoint
local PointVec3 = Airplane:GetPointVec3()
Template.x = PointVec3.x
Template.y = PointVec3.z
Template.route.points = Points
local GroupSpawned = Airplane:Respawn(Template)
end
end
end
--- On after Home event. Aircraft will be routed to their home base.
-- @param #AI_CARGO_AIRPLANE self
-- @param Wrapper.Group#GROUP Airplane The cargo plane.
-- @param From From state.
-- @param Event Event.
-- @param To To State.
-- @param Core.Point#COORDINATE Coordinate Home place (not used).
-- @param #number Speed Speed in km/h to fly to the home airbase (zone). Default is 80% of max possible speed the unit can go.
-- @param #number Height Height in meters to move to the home coordinate.
-- @param Core.Zone#ZONE_AIRBASE HomeZone The home airbase (zone) where the plane should return to.
function AI_CARGO_AIRPLANE:onafterHome(Airplane, From, Event, To, Coordinate, Speed, Height, HomeZone )
if Airplane and Airplane:IsAlive() then
-- We are going home!
self.RouteHome = true
-- Home Base.
local HomeBase=HomeZone:GetAirbase()
self.Airbase=HomeBase
-- Now route the airplane home
self:Route( Airplane, HomeBase, Speed, Height )
end
end

File diff suppressed because it is too large Load Diff

View File

@@ -1,263 +0,0 @@
--- **AI** - Models the intelligent transportation of infantry and other cargo using APCs.
--
-- ## Features:
--
-- * Quickly transport cargo to various deploy zones using ground vehicles (APCs, trucks ...).
-- * Various @{Cargo.Cargo#CARGO} types can be transported. These are infantry groups and crates.
-- * Define a list of deploy zones of various types to transport the cargo to.
-- * The vehicles follow the roads to ensure the fastest possible cargo transportation over the ground.
-- * Multiple vehicles can transport multiple cargo as one vehicle group.
-- * Multiple vehicle groups can be enabled as one collaborating transportation process.
-- * Infantry loaded as cargo, will unboard in case enemies are nearby and will help defending the vehicles.
-- * Different ranges can be setup for enemy defenses.
-- * Different options can be setup to tweak the cargo transporation behaviour.
--
-- ===
--
-- ## Test Missions:
--
-- Test missions can be located on the main GITHUB site.
--
-- [FlightControl-Master/MOOSE_MISSIONS/AID - AI Dispatching/AID-CGO - AI Cargo Dispatching/]
-- (https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/AID%20-%20AI%20Dispatching/AID-CGO%20-%20AI%20Cargo%20Dispatching)
--
-- ===
--
-- ### Author: **FlightControl**
--
-- ===
--
-- @module AI.AI_Cargo_Dispatcher_APC
-- @image AI_Cargo_Dispatching_For_APC.JPG
-- @type AI_CARGO_DISPATCHER_APC
-- @extends AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER
--- A dynamic cargo transportation capability for AI groups.
--
-- Armoured Personnel APCs (APC), Trucks, Jeeps and other carrier equipment can be mobilized to intelligently transport infantry and other cargo within the simulation.
--
-- The AI_CARGO_DISPATCHER_APC module is derived from the AI_CARGO_DISPATCHER module.
--
-- ## Note! In order to fully understand the mechanisms of the AI_CARGO_DISPATCHER_APC class, it is recommended that you first consult and READ the documentation of the @{AI.AI_Cargo_Dispatcher} module!!!
--
-- Especially to learn how to **Tailor the different cargo handling events**, this will be very useful!
--
-- On top, the AI_CARGO_DISPATCHER_APC class uses the @{Cargo.Cargo} capabilities within the MOOSE framework.
-- Also ensure that you fully understand how to declare and setup Cargo objects within the MOOSE framework before using this class.
-- CARGO derived objects must be declared within the mission to make the AI_CARGO_DISPATCHER_HELICOPTER object recognize the cargo.
--
--
-- # 1) AI_CARGO_DISPATCHER_APC constructor.
--
-- * @{#AI_CARGO_DISPATCHER_APC.New}(): Creates a new AI_CARGO_DISPATCHER_APC object.
--
-- ---
--
-- # 2) AI_CARGO_DISPATCHER_APC is a Finite State Machine.
--
-- This section must be read as follows. Each of the rows indicate a state transition, triggered through an event, and with an ending state of the event was executed.
-- The first column is the **From** state, the second column the **Event**, and the third column the **To** state.
--
-- So, each of the rows have the following structure.
--
-- * **From** => **Event** => **To**
--
-- Important to know is that an event can only be executed if the **current state** is the **From** state.
-- This, when an **Event** that is being triggered has a **From** state that is equal to the **Current** state of the state machine, the event will be executed,
-- and the resulting state will be the **To** state.
--
-- These are the different possible state transitions of this state machine implementation:
--
-- * Idle => Start => Monitoring
-- * Monitoring => Monitor => Monitoring
-- * Monitoring => Stop => Idle
--
-- * Monitoring => Pickup => Monitoring
-- * Monitoring => Load => Monitoring
-- * Monitoring => Loading => Monitoring
-- * Monitoring => Loaded => Monitoring
-- * Monitoring => PickedUp => Monitoring
-- * Monitoring => Deploy => Monitoring
-- * Monitoring => Unload => Monitoring
-- * Monitoring => Unloaded => Monitoring
-- * Monitoring => Deployed => Monitoring
-- * Monitoring => Home => Monitoring
--
--
-- ## 2.1) AI_CARGO_DISPATCHER States.
--
-- * **Monitoring**: The process is dispatching.
-- * **Idle**: The process is idle.
--
-- ## 2.2) AI_CARGO_DISPATCHER Events.
--
-- * **Start**: Start the transport process.
-- * **Stop**: Stop the transport process.
-- * **Monitor**: Monitor and take action.
--
-- * **Pickup**: Pickup cargo.
-- * **Load**: Load the cargo.
-- * **Loading**: The dispatcher is coordinating the loading of a cargo.
-- * **Loaded**: Flag that the cargo is loaded.
-- * **PickedUp**: The dispatcher has loaded all requested cargo into the CarrierGroup.
-- * **Deploy**: Deploy cargo to a location.
-- * **Unload**: Unload the cargo.
-- * **Unloaded**: Flag that the cargo is unloaded.
-- * **Deployed**: All cargo is unloaded from the carriers in the group.
-- * **Home**: A Carrier is going home.
--
-- ## 2.3) Enhance your mission scripts with **Tailored** Event Handling!
--
-- Within your mission, you can capture these events when triggered, and tailor the events with your own code!
-- Check out the @{AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER} class at chapter 3 for details on the different event handlers that are available and how to use them.
--
-- **There are a lot of templates available that allows you to quickly setup an event handler for a specific event type!**
--
-- ---
--
-- # 3) Set the pickup parameters.
--
-- Several parameters can be set to pickup cargo:
--
-- * @{#AI_CARGO_DISPATCHER_APC.SetPickupRadius}(): Sets or randomizes the pickup location for the APC around the cargo coordinate in a radius defined an outer and optional inner radius.
-- * @{#AI_CARGO_DISPATCHER_APC.SetPickupSpeed}(): Set the speed or randomizes the speed in km/h to pickup the cargo.
--
-- # 4) Set the deploy parameters.
--
-- Several parameters can be set to deploy cargo:
--
-- * @{#AI_CARGO_DISPATCHER_APC.SetDeployRadius}(): Sets or randomizes the deploy location for the APC around the cargo coordinate in a radius defined an outer and an optional inner radius.
-- * @{#AI_CARGO_DISPATCHER_APC.SetDeploySpeed}(): Set the speed or randomizes the speed in km/h to deploy the cargo.
--
-- # 5) Set the home zone when there isn't any more cargo to pickup.
--
-- A home zone can be specified to where the APCs will move when there isn't any cargo left for pickup.
-- Use @{#AI_CARGO_DISPATCHER_APC.SetHomeZone}() to specify the home zone.
--
-- If no home zone is specified, the APCs will wait near the deploy zone for a new pickup command.
--
-- # Developer Note
--
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
-- Therefore, this class is considered to be deprecated
--
-- ===
--
-- @field #AI_CARGO_DISPATCHER_APC
AI_CARGO_DISPATCHER_APC = {
ClassName = "AI_CARGO_DISPATCHER_APC",
}
--- Creates a new AI_CARGO_DISPATCHER_APC object.
-- @param #AI_CARGO_DISPATCHER_APC self
-- @param Core.Set#SET_GROUP APCSet The set of @{Wrapper.Group#GROUP} objects of vehicles, trucks, APCs that will transport the cargo.
-- @param Core.Set#SET_CARGO CargoSet The set of @{Cargo.Cargo#CARGO} objects, which can be CARGO_GROUP, CARGO_CRATE, CARGO_SLINGLOAD objects.
-- @param Core.Set#SET_ZONE PickupZoneSet (optional) The set of pickup zones, which are used to where the cargo can be picked up by the APCs. If nil, then cargo can be picked up everywhere.
-- @param Core.Set#SET_ZONE DeployZoneSet The set of deploy zones, which are used to where the cargo will be deployed by the APCs.
-- @param DCS#Distance CombatRadius The cargo will be unloaded from the APC and engage the enemy if the enemy is within CombatRadius range. The radius is in meters, the default value is 500 meters.
-- @return #AI_CARGO_DISPATCHER_APC
-- @usage
--
-- -- An AI dispatcher object for a vehicle squadron, moving infantry from pickup zones to deploy zones.
--
-- local SetCargoInfantry = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart()
-- local SetAPC = SET_GROUP:New():FilterPrefixes( "APC" ):FilterStart()
-- local SetDeployZones = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart()
--
-- AICargoDispatcherAPC = AI_CARGO_DISPATCHER_APC:New( SetAPC, SetCargoInfantry, nil, SetDeployZones )
-- AICargoDispatcherAPC:Start()
--
function AI_CARGO_DISPATCHER_APC:New( APCSet, CargoSet, PickupZoneSet, DeployZoneSet, CombatRadius )
local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:New( APCSet, CargoSet, PickupZoneSet, DeployZoneSet ) ) -- #AI_CARGO_DISPATCHER_APC
self:SetDeploySpeed( 120, 70 )
self:SetPickupSpeed( 120, 70 )
self:SetPickupRadius( 0, 0 )
self:SetDeployRadius( 0, 0 )
self:SetPickupHeight()
self:SetDeployHeight()
self:SetCombatRadius( CombatRadius )
return self
end
--- AI cargo
-- @param #AI_CARGO_DISPATCHER_APC self
-- @param Wrapper.Group#GROUP APC The APC carrier.
-- @param Core.Set#SET_CARGO CargoSet Cargo set.
-- @return AI.AI_Cargo_APC#AI_CARGO_DISPATCHER_APC AI cargo APC object.
function AI_CARGO_DISPATCHER_APC:AICargo( APC, CargoSet )
local aicargoapc=AI_CARGO_APC:New(APC, CargoSet, self.CombatRadius)
aicargoapc:SetDeployOffRoad(self.deployOffroad, self.deployFormation)
aicargoapc:SetPickupOffRoad(self.pickupOffroad, self.pickupFormation)
return aicargoapc
end
--- Enable/Disable unboarding of cargo (infantry) when enemies are nearby (to help defend the carrier).
-- This is only valid for APCs and trucks etc, thus ground vehicles.
-- @param #AI_CARGO_DISPATCHER_APC self
-- @param #number CombatRadius Provide the combat radius to defend the carrier by unboarding the cargo when enemies are nearby.
-- When the combat radius is 0 (default), no defense will happen of the carrier.
-- When the combat radius is not provided, no defense will happen!
-- @return #AI_CARGO_DISPATCHER_APC
-- @usage
--
-- -- Disembark the infantry when the carrier is under attack.
-- AICargoDispatcher:SetCombatRadius( 500 )
--
-- -- Keep the cargo in the carrier when the carrier is under attack.
-- AICargoDispatcher:SetCombatRadius( 0 )
function AI_CARGO_DISPATCHER_APC:SetCombatRadius( CombatRadius )
self.CombatRadius = CombatRadius or 0
return self
end
--- Set whether the carrier will *not* use roads to *pickup* and *deploy* the cargo.
-- @param #AI_CARGO_DISPATCHER_APC self
-- @param #boolean Offroad If true, carrier will not use roads.
-- @param #number Formation Offroad formation used. Default is `ENUMS.Formation.Vehicle.Offroad`.
-- @return #AI_CARGO_DISPATCHER_APC self
function AI_CARGO_DISPATCHER_APC:SetOffRoad(Offroad, Formation)
self:SetPickupOffRoad(Offroad, Formation)
self:SetDeployOffRoad(Offroad, Formation)
return self
end
--- Set whether the carrier will *not* use roads to *pickup* the cargo.
-- @param #AI_CARGO_DISPATCHER_APC self
-- @param #boolean Offroad If true, carrier will not use roads.
-- @param #number Formation Offroad formation used. Default is `ENUMS.Formation.Vehicle.Offroad`.
-- @return #AI_CARGO_DISPATCHER_APC self
function AI_CARGO_DISPATCHER_APC:SetPickupOffRoad(Offroad, Formation)
self.pickupOffroad=Offroad
self.pickupFormation=Formation or ENUMS.Formation.Vehicle.OffRoad
return self
end
--- Set whether the carrier will *not* use roads to *deploy* the cargo.
-- @param #AI_CARGO_DISPATCHER_APC self
-- @param #boolean Offroad If true, carrier will not use roads.
-- @param #number Formation Offroad formation used. Default is `ENUMS.Formation.Vehicle.Offroad`.
-- @return #AI_CARGO_DISPATCHER_APC self
function AI_CARGO_DISPATCHER_APC:SetDeployOffRoad(Offroad, Formation)
self.deployOffroad=Offroad
self.deployFormation=Formation or ENUMS.Formation.Vehicle.OffRoad
return self
end

View File

@@ -1,169 +0,0 @@
--- **AI** - Models the intelligent transportation of infantry and other cargo using Planes.
--
-- ## Features:
--
-- * The airplanes will fly towards the pickup airbases to pickup the cargo.
-- * The airplanes will fly towards the deploy airbases to deploy the cargo.
--
-- ===
--
-- ## Test Missions:
--
-- Test missions can be located on the main GITHUB site.
--
-- [FlightControl-Master/MOOSE_MISSIONS/AID - AI Dispatching/AID-CGO - AI Cargo Dispatching/]
-- (https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/AID%20-%20AI%20Dispatching/AID-CGO%20-%20AI%20Cargo%20Dispatching)
--
-- ===
--
-- ### Author: **FlightControl**
--
-- ===
--
-- @module AI.AI_Cargo_Dispatcher_Airplane
-- @image AI_Cargo_Dispatching_For_Airplanes.JPG
-- @type AI_CARGO_DISPATCHER_AIRPLANE
-- @extends AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER
--- Brings a dynamic cargo handling capability for AI groups.
--
-- Airplanes can be mobilized to intelligently transport infantry and other cargo within the simulation.
--
-- The AI_CARGO_DISPATCHER_AIRPLANE module is derived from the AI_CARGO_DISPATCHER module.
--
-- ## Note! In order to fully understand the mechanisms of the AI_CARGO_DISPATCHER_AIRPLANE class, it is recommended that you first consult and READ the documentation of the @{AI.AI_Cargo_Dispatcher} module!!!**
--
-- Especially to learn how to **Tailor the different cargo handling events**, this will be very useful!
--
-- On top, the AI_CARGO_DISPATCHER_AIRPLANE class uses the @{Cargo.Cargo} capabilities within the MOOSE framework.
-- Also ensure that you fully understand how to declare and setup Cargo objects within the MOOSE framework before using this class.
-- CARGO derived objects must be declared within the mission to make the AI_CARGO_DISPATCHER_HELICOPTER object recognize the cargo.
--
-- # 1) AI_CARGO_DISPATCHER_AIRPLANE constructor.
--
-- * @{#AI_CARGO_DISPATCHER_AIRPLANE.New}(): Creates a new AI_CARGO_DISPATCHER_AIRPLANE object.
--
-- ---
--
-- # 2) AI_CARGO_DISPATCHER_AIRPLANE is a Finite State Machine.
--
-- This section must be read as follows. Each of the rows indicate a state transition, triggered through an event, and with an ending state of the event was executed.
-- The first column is the **From** state, the second column the **Event**, and the third column the **To** state.
--
-- So, each of the rows have the following structure.
--
-- * **From** => **Event** => **To**
--
-- Important to know is that an event can only be executed if the **current state** is the **From** state.
-- This, when an **Event** that is being triggered has a **From** state that is equal to the **Current** state of the state machine, the event will be executed,
-- and the resulting state will be the **To** state.
--
-- These are the different possible state transitions of this state machine implementation:
--
-- * Idle => Start => Monitoring
-- * Monitoring => Monitor => Monitoring
-- * Monitoring => Stop => Idle
--
-- * Monitoring => Pickup => Monitoring
-- * Monitoring => Load => Monitoring
-- * Monitoring => Loading => Monitoring
-- * Monitoring => Loaded => Monitoring
-- * Monitoring => PickedUp => Monitoring
-- * Monitoring => Deploy => Monitoring
-- * Monitoring => Unload => Monitoring
-- * Monitoring => Unloaded => Monitoring
-- * Monitoring => Deployed => Monitoring
-- * Monitoring => Home => Monitoring
--
--
-- ## 2.1) AI_CARGO_DISPATCHER States.
--
-- * **Monitoring**: The process is dispatching.
-- * **Idle**: The process is idle.
--
-- ## 2.2) AI_CARGO_DISPATCHER Events.
--
-- * **Start**: Start the transport process.
-- * **Stop**: Stop the transport process.
-- * **Monitor**: Monitor and take action.
--
-- * **Pickup**: Pickup cargo.
-- * **Load**: Load the cargo.
-- * **Loading**: The dispatcher is coordinating the loading of a cargo.
-- * **Loaded**: Flag that the cargo is loaded.
-- * **PickedUp**: The dispatcher has loaded all requested cargo into the CarrierGroup.
-- * **Deploy**: Deploy cargo to a location.
-- * **Unload**: Unload the cargo.
-- * **Unloaded**: Flag that the cargo is unloaded.
-- * **Deployed**: All cargo is unloaded from the carriers in the group.
-- * **Home**: A Carrier is going home.
--
-- ## 2.3) Enhance your mission scripts with **Tailored** Event Handling!
--
-- Within your mission, you can capture these events when triggered, and tailor the events with your own code!
-- Check out the @{AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER} class at chapter 3 for details on the different event handlers that are available and how to use them.
--
-- **There are a lot of templates available that allows you to quickly setup an event handler for a specific event type!**
--
--
-- # Developer Note
--
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
-- Therefore, this class is considered to be deprecated
--
--
-- @field #AI_CARGO_DISPATCHER_AIRPLANE
AI_CARGO_DISPATCHER_AIRPLANE = {
ClassName = "AI_CARGO_DISPATCHER_AIRPLANE",
}
--- Creates a new AI_CARGO_DISPATCHER_AIRPLANE object.
-- @param #AI_CARGO_DISPATCHER_AIRPLANE self
-- @param Core.Set#SET_GROUP AirplaneSet The set of @{Wrapper.Group#GROUP} objects of airplanes that will transport the cargo.
-- @param Core.Set#SET_CARGO CargoSet The set of @{Cargo.Cargo#CARGO} objects, which can be CARGO_GROUP, CARGO_CRATE, CARGO_SLINGLOAD objects.
-- @param Core.Zone#SET_ZONE PickupZoneSet The set of zone airbases where the cargo has to be picked up.
-- @param Core.Zone#SET_ZONE DeployZoneSet The set of zone airbases where the cargo is deployed. Choice for each cargo is random.
-- @return #AI_CARGO_DISPATCHER_AIRPLANE self
-- @usage
--
-- -- An AI dispatcher object for an airplane squadron, moving infantry and vehicles from pickup airbases to deploy airbases.
--
-- local CargoInfantrySet = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart()
-- local AirplanesSet = SET_GROUP:New():FilterPrefixes( "Airplane" ):FilterStart()
-- local PickupZoneSet = SET_ZONE:New()
-- local DeployZoneSet = SET_ZONE:New()
--
-- PickupZoneSet:AddZone( ZONE_AIRBASE:New( AIRBASE.Caucasus.Gudauta ) )
-- DeployZoneSet:AddZone( ZONE_AIRBASE:New( AIRBASE.Caucasus.Sochi_Adler ) )
-- DeployZoneSet:AddZone( ZONE_AIRBASE:New( AIRBASE.Caucasus.Maykop_Khanskaya ) )
-- DeployZoneSet:AddZone( ZONE_AIRBASE:New( AIRBASE.Caucasus.Mineralnye_Vody ) )
-- DeployZoneSet:AddZone( ZONE_AIRBASE:New( AIRBASE.Caucasus.Vaziani ) )
--
-- AICargoDispatcherAirplanes = AI_CARGO_DISPATCHER_AIRPLANE:New( AirplanesSet, CargoInfantrySet, PickupZoneSet, DeployZoneSet )
-- AICargoDispatcherAirplanes:Start()
--
function AI_CARGO_DISPATCHER_AIRPLANE:New( AirplaneSet, CargoSet, PickupZoneSet, DeployZoneSet )
local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:New( AirplaneSet, CargoSet, PickupZoneSet, DeployZoneSet ) ) -- #AI_CARGO_DISPATCHER_AIRPLANE
self:SetPickupSpeed( 1200, 600 )
self:SetDeploySpeed( 1200, 600 )
self:SetPickupRadius( 0, 0 )
self:SetDeployRadius( 0, 0 )
self:SetPickupHeight( 8000, 6000 )
self:SetDeployHeight( 8000, 6000 )
self:SetMonitorTimeInterval( 600 )
return self
end
function AI_CARGO_DISPATCHER_AIRPLANE:AICargo( Airplane, CargoSet )
return AI_CARGO_AIRPLANE:New( Airplane, CargoSet )
end

View File

@@ -1,199 +0,0 @@
--- **AI** - Models the intelligent transportation of infantry and other cargo using Helicopters.
--
-- ## Features:
--
-- * The helicopters will fly towards the pickup locations to pickup the cargo.
-- * The helicopters will fly towards the deploy zones to deploy the cargo.
-- * Precision deployment as well as randomized deployment within the deploy zones are possible.
-- * Helicopters will orbit the deploy zones when there is no space for landing until the deploy zone is free.
--
-- ===
--
-- ## Test Missions:
--
-- Test missions can be located on the main GITHUB site.
--
-- [FlightControl-Master/MOOSE_MISSIONS/AID - AI Dispatching/AID-CGO - AI Cargo Dispatching/]
-- (https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/AID%20-%20AI%20Dispatching/AID-CGO%20-%20AI%20Cargo%20Dispatching)
--
-- ===
--
-- ### Author: **FlightControl**
--
-- ===
--
-- @module AI.AI_Cargo_Dispatcher_Helicopter
-- @image AI_Cargo_Dispatching_For_Helicopters.JPG
-- @type AI_CARGO_DISPATCHER_HELICOPTER
-- @extends AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER
--- A dynamic cargo handling capability for AI helicopter groups.
--
-- Helicopters can be mobilized to intelligently transport infantry and other cargo within the simulation.
--
--
-- The AI_CARGO_DISPATCHER_HELICOPTER module is derived from the AI_CARGO_DISPATCHER module.
--
-- ## Note! In order to fully understand the mechanisms of the AI_CARGO_DISPATCHER_HELICOPTER class, it is recommended that you first consult and READ the documentation of the @{AI.AI_Cargo_Dispatcher} module!!!**
--
-- Especially to learn how to **Tailor the different cargo handling events**, this will be very useful!
--
-- On top, the AI_CARGO_DISPATCHER_HELICOPTER class uses the @{Cargo.Cargo} capabilities within the MOOSE framework.
-- Also ensure that you fully understand how to declare and setup Cargo objects within the MOOSE framework before using this class.
-- CARGO derived objects must be declared within the mission to make the AI_CARGO_DISPATCHER_HELICOPTER object recognize the cargo.
--
-- ---
--
-- # 1. AI\_CARGO\_DISPATCHER\_HELICOPTER constructor.
--
-- * @{#AI_CARGO_DISPATCHER\_HELICOPTER.New}(): Creates a new AI\_CARGO\_DISPATCHER\_HELICOPTER object.
--
-- ---
--
-- # 2. AI\_CARGO\_DISPATCHER\_HELICOPTER is a Finite State Machine.
--
-- This section must be read as follows. Each of the rows indicate a state transition, triggered through an event, and with an ending state of the event was executed.
-- The first column is the **From** state, the second column the **Event**, and the third column the **To** state.
--
-- So, each of the rows have the following structure.
--
-- * **From** => **Event** => **To**
--
-- Important to know is that an event can only be executed if the **current state** is the **From** state.
-- This, when an **Event** that is being triggered has a **From** state that is equal to the **Current** state of the state machine, the event will be executed,
-- and the resulting state will be the **To** state.
--
-- These are the different possible state transitions of this state machine implementation:
--
-- * Idle => Start => Monitoring
-- * Monitoring => Monitor => Monitoring
-- * Monitoring => Stop => Idle
--
-- * Monitoring => Pickup => Monitoring
-- * Monitoring => Load => Monitoring
-- * Monitoring => Loading => Monitoring
-- * Monitoring => Loaded => Monitoring
-- * Monitoring => PickedUp => Monitoring
-- * Monitoring => Deploy => Monitoring
-- * Monitoring => Unload => Monitoring
-- * Monitoring => Unloaded => Monitoring
-- * Monitoring => Deployed => Monitoring
-- * Monitoring => Home => Monitoring
--
--
-- ## 2.1) AI_CARGO_DISPATCHER States.
--
-- * **Monitoring**: The process is dispatching.
-- * **Idle**: The process is idle.
--
-- ## 2.2) AI_CARGO_DISPATCHER Events.
--
-- * **Start**: Start the transport process.
-- * **Stop**: Stop the transport process.
-- * **Monitor**: Monitor and take action.
--
-- * **Pickup**: Pickup cargo.
-- * **Load**: Load the cargo.
-- * **Loading**: The dispatcher is coordinating the loading of a cargo.
-- * **Loaded**: Flag that the cargo is loaded.
-- * **PickedUp**: The dispatcher has loaded all requested cargo into the CarrierGroup.
-- * **Deploy**: Deploy cargo to a location.
-- * **Unload**: Unload the cargo.
-- * **Unloaded**: Flag that the cargo is unloaded.
-- * **Deployed**: All cargo is unloaded from the carriers in the group.
-- * **Home**: A Carrier is going home.
--
-- ## 2.3) Enhance your mission scripts with **Tailored** Event Handling!
--
-- Within your mission, you can capture these events when triggered, and tailor the events with your own code!
-- Check out the @{AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER} class at chapter 3 for details on the different event handlers that are available and how to use them.
--
-- **There are a lot of templates available that allows you to quickly setup an event handler for a specific event type!**
--
-- ---
--
-- ## 3. Set the pickup parameters.
--
-- Several parameters can be set to pickup cargo:
--
-- * @{#AI_CARGO_DISPATCHER_HELICOPTER.SetPickupRadius}(): Sets or randomizes the pickup location for the helicopter around the cargo coordinate in a radius defined an outer and optional inner radius.
-- * @{#AI_CARGO_DISPATCHER_HELICOPTER.SetPickupSpeed}(): Set the speed or randomizes the speed in km/h to pickup the cargo.
-- * @{#AI_CARGO_DISPATCHER_HELICOPTER.SetPickupHeight}(): Set the height or randomizes the height in meters to pickup the cargo.
--
-- ---
--
-- ## 4. Set the deploy parameters.
--
-- Several parameters can be set to deploy cargo:
--
-- * @{#AI_CARGO_DISPATCHER_HELICOPTER.SetDeployRadius}(): Sets or randomizes the deploy location for the helicopter around the cargo coordinate in a radius defined an outer and an optional inner radius.
-- * @{#AI_CARGO_DISPATCHER_HELICOPTER.SetDeploySpeed}(): Set the speed or randomizes the speed in km/h to deploy the cargo.
-- * @{#AI_CARGO_DISPATCHER_HELICOPTER.SetDeployHeight}(): Set the height or randomizes the height in meters to deploy the cargo.
--
-- ---
--
-- ## 5. Set the home zone when there isn't any more cargo to pickup.
--
-- A home zone can be specified to where the Helicopters will move when there isn't any cargo left for pickup.
-- Use @{#AI_CARGO_DISPATCHER_HELICOPTER.SetHomeZone}() to specify the home zone.
--
-- If no home zone is specified, the helicopters will wait near the deploy zone for a new pickup command.
--
-- # Developer Note
--
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
-- Therefore, this class is considered to be deprecated
--
-- ===
--
-- @field #AI_CARGO_DISPATCHER_HELICOPTER
AI_CARGO_DISPATCHER_HELICOPTER = {
ClassName = "AI_CARGO_DISPATCHER_HELICOPTER",
}
--- Creates a new AI_CARGO_DISPATCHER_HELICOPTER object.
-- @param #AI_CARGO_DISPATCHER_HELICOPTER self
-- @param Core.Set#SET_GROUP HelicopterSet The set of @{Wrapper.Group#GROUP} objects of helicopters that will transport the cargo.
-- @param Core.Set#SET_CARGO CargoSet The set of @{Cargo.Cargo#CARGO} objects, which can be CARGO_GROUP, CARGO_CRATE, CARGO_SLINGLOAD objects.
-- @param Core.Set#SET_ZONE PickupZoneSet (optional) The set of pickup zones, which are used to where the cargo can be picked up by the APCs. If nil, then cargo can be picked up everywhere.
-- @param Core.Set#SET_ZONE DeployZoneSet The set of deploy zones, which are used to where the cargo will be deployed by the Helicopters.
-- @return #AI_CARGO_DISPATCHER_HELICOPTER
-- @usage
--
-- -- An AI dispatcher object for a helicopter squadron, moving infantry from pickup zones to deploy zones.
--
-- local SetCargoInfantry = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart()
-- local SetHelicopter = SET_GROUP:New():FilterPrefixes( "Helicopter" ):FilterStart()
-- local SetPickupZones = SET_ZONE:New():FilterPrefixes( "Pickup" ):FilterStart()
-- local SetDeployZones = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart()
--
-- AICargoDispatcherHelicopter = AI_CARGO_DISPATCHER_HELICOPTER:New( SetHelicopter, SetCargoInfantry, SetPickupZones, SetDeployZones )
-- AICargoDispatcherHelicopter:Start()
--
function AI_CARGO_DISPATCHER_HELICOPTER:New( HelicopterSet, CargoSet, PickupZoneSet, DeployZoneSet )
local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:New( HelicopterSet, CargoSet, PickupZoneSet, DeployZoneSet ) ) -- #AI_CARGO_DISPATCHER_HELICOPTER
self:SetPickupSpeed( 350, 150 )
self:SetDeploySpeed( 350, 150 )
self:SetPickupRadius( 40, 12 )
self:SetDeployRadius( 40, 12 )
self:SetPickupHeight( 500, 200 )
self:SetDeployHeight( 500, 200 )
return self
end
function AI_CARGO_DISPATCHER_HELICOPTER:AICargo( Helicopter, CargoSet )
local dispatcher = AI_CARGO_HELICOPTER:New( Helicopter, CargoSet )
dispatcher:SetLandingSpeedAndHeight(27, 6)
return dispatcher
end

View File

@@ -1,198 +0,0 @@
--- **AI** - Models the intelligent transportation of infantry and other cargo using Ships.
--
-- ## Features:
--
-- * Transport cargo to various deploy zones using naval vehicles.
-- * Various @{Cargo.Cargo#CARGO} types can be transported, including infantry, vehicles, and crates.
-- * Define a deploy zone of various types to determine the destination of the cargo.
-- * Ships will follow shipping lanes as defined in the Mission Editor.
-- * Multiple ships can transport multiple cargo as a single group.
--
-- ===
--
-- ## Test Missions:
--
-- NEED TO DO
--
-- ===
--
-- ### Author: **acrojason** (derived from AI_Cargo_Dispatcher_APC by FlightControl)
--
-- ===
--
-- @module AI.AI_Cargo_Dispatcher_Ship
-- @image AI_Cargo_Dispatcher.JPG
-- @type AI_CARGO_DISPATCHER_SHIP
-- @extends AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER
--- A dynamic cargo transportation capability for AI groups.
--
-- Naval vessels can be mobilized to semi-intelligently transport cargo within the simulation.
--
-- The AI_CARGO_DISPATCHER_SHIP module is derived from the AI_CARGO_DISPATCHER module.
--
-- ## Note! In order to fully understand the mechanisms of the AI_CARGO_DISPATCHER_SHIP class, it is recommended that you first consult and READ the documentation of the @{AI.AI_Cargo_Dispatcher} module!!!
--
-- This will be particularly helpful in order to determine how to **Tailor the different cargo handling events**.
--
-- The AI_CARGO_DISPATCHER_SHIP class uses the @{Cargo.Cargo} capabilities within the MOOSE framework.
-- Also ensure that you fully understand how to declare and setup Cargo objects within the MOOSE framework before using this class.
-- CARGO derived objects must generally be declared within the mission to make the AI_CARGO_DISPATCHER_SHIP object recognize the cargo.
--
--
-- # 1) AI_CARGO_DISPATCHER_SHIP constructor.
--
-- * @{#AI_CARGO_DISPATCHER_SHIP.New}(): Creates a new AI_CARGO_DISPATCHER_SHIP object.
--
-- ---
--
-- # 2) AI_CARGO_DISPATCHER_SHIP is a Finite State Machine.
--
-- This section must be read as follows... Each of the rows indicate a state transition, triggered through an event, and with an ending state of the event was executed.
-- The first column is the **From** state, the second column the **Event**, and the third column the **To** state.
--
-- So, each of the rows have the following structure.
--
-- * **From** => **Event** => **To**
--
-- Important to know is that an event can only be executed if the **current state** is the **From** state.
-- This, when an **Event** that is being triggered has a **From** state that is equal to the **Current** state of the state machine, the event will be executed,
-- and the resulting state will be the **To** state.
--
-- These are the different possible state transitions of this state machine implementation:
--
-- * Idle => Start => Monitoring
-- * Monitoring => Monitor => Monitoring
-- * Monitoring => Stop => Idle
--
-- * Monitoring => Pickup => Monitoring
-- * Monitoring => Load => Monitoring
-- * Monitoring => Loading => Monitoring
-- * Monitoring => Loaded => Monitoring
-- * Monitoring => PickedUp => Monitoring
-- * Monitoring => Deploy => Monitoring
-- * Monitoring => Unload => Monitoring
-- * Monitoring => Unloaded => Monitoring
-- * Monitoring => Deployed => Monitoring
-- * Monitoring => Home => Monitoring
--
--
-- ## 2.1) AI_CARGO_DISPATCHER States.
--
-- * **Monitoring**: The process is dispatching.
-- * **Idle**: The process is idle.
--
-- ## 2.2) AI_CARGO_DISPATCHER Events.
--
-- * **Start**: Start the transport process.
-- * **Stop**: Stop the transport process.
-- * **Monitor**: Monitor and take action.
--
-- * **Pickup**: Pickup cargo.
-- * **Load**: Load the cargo.
-- * **Loading**: The dispatcher is coordinating the loading of a cargo.
-- * **Loaded**: Flag that the cargo is loaded.
-- * **PickedUp**: The dispatcher has loaded all requested cargo into the CarrierGroup.
-- * **Deploy**: Deploy cargo to a location.
-- * **Unload**: Unload the cargo.
-- * **Unloaded**: Flag that the cargo is unloaded.
-- * **Deployed**: All cargo is unloaded from the carriers in the group.
-- * **Home**: A Carrier is going home.
--
-- ## 2.3) Enhance your mission scripts with **Tailored** Event Handling!
--
-- Within your mission, you can capture these events when triggered, and tailor the events with your own code!
-- Check out the @{AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER} class at chapter 3 for details on the different event handlers that are available and how to use them.
--
-- **There are a lot of templates available that allows you to quickly setup an event handler for a specific event type!**
--
-- ---
--
-- # 3) Set the pickup parameters.
--
-- Several parameters can be set to pickup cargo:
--
-- * @{#AI_CARGO_DISPATCHER_SHIP.SetPickupRadius}(): Sets or randomizes the pickup location for the Ship around the cargo coordinate in a radius defined an outer and optional inner radius.
-- * @{#AI_CARGO_DISPATCHER_SHIP.SetPickupSpeed}(): Set the speed or randomizes the speed in km/h to pickup the cargo.
--
-- # 4) Set the deploy parameters.
--
-- Several parameters can be set to deploy cargo:
--
-- * @{#AI_CARGO_DISPATCHER_SHIP.SetDeployRadius}(): Sets or randomizes the deploy location for the Ship around the cargo coordinate in a radius defined an outer and an optional inner radius.
-- * @{#AI_CARGO_DISPATCHER_SHIP.SetDeploySpeed}(): Set the speed or randomizes the speed in km/h to deploy the cargo.
--
-- # 5) Set the home zone when there isn't any more cargo to pickup.
--
-- A home zone can be specified to where the Ship will move when there isn't any cargo left for pickup.
-- Use @{#AI_CARGO_DISPATCHER_SHIP.SetHomeZone}() to specify the home zone.
--
-- If no home zone is specified, the Ship will wait near the deploy zone for a new pickup command.
--
-- # Developer Note
--
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
-- Therefore, this class is considered to be deprecated
--
-- ===
--
-- @field #AI_CARGO_DISPATCHER_SHIP
AI_CARGO_DISPATCHER_SHIP = {
ClassName = "AI_CARGO_DISPATCHER_SHIP"
}
--- Creates a new AI_CARGO_DISPATCHER_SHIP object.
-- @param #AI_CARGO_DISPATCHER_SHIP self
-- @param Core.Set#SET_GROUP ShipSet The set of @{Wrapper.Group#GROUP} objects of Ships that will transport the cargo
-- @param Core.Set#SET_CARGO CargoSet The set of @{Cargo.Cargo#CARGO} objects, which can be CARGO_GROUP, CARGO_CRATE, or CARGO_SLINGLOAD objects.
-- @param Core.Set#SET_ZONE PickupZoneSet The set of pickup zones which are used to determine from where the cargo can be picked up by the Ship.
-- @param Core.Set#SET_ZONE DeployZoneSet The set of deploy zones which determine where the cargo will be deployed by the Ship.
-- @param #table ShippingLane Table containing list of Shipping Lanes to be used
-- @return #AI_CARGO_DISPATCHER_SHIP
-- @usage
--
-- -- An AI dispatcher object for a naval group, moving cargo from pickup zones to deploy zones via a predetermined Shipping Lane
--
-- local SetCargoInfantry = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart()
-- local SetShip = SET_GROUP:New():FilterPrefixes( "Ship" ):FilterStart()
-- local SetPickupZones = SET_ZONE:New():FilterPrefixes( "Pickup" ):FilterStart()
-- local SetDeployZones = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart()
-- NEED MORE THOUGHT - ShippingLane is part of Warehouse.......
-- local ShippingLane = SET_GROUP:New():FilterPrefixes( "ShippingLane" ):FilterOnce():GetSetObjects()
--
-- AICargoDispatcherShip = AI_CARGO_DISPATCHER_SHIP:New( SetShip, SetCargoInfantry, SetPickupZones, SetDeployZones, ShippingLane )
-- AICargoDispatcherShip:Start()
--
function AI_CARGO_DISPATCHER_SHIP:New( ShipSet, CargoSet, PickupZoneSet, DeployZoneSet, ShippingLane )
local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:New( ShipSet, CargoSet, PickupZoneSet, DeployZoneSet ) )
self:SetPickupSpeed( 60, 10 )
self:SetDeploySpeed( 60, 10 )
self:SetPickupRadius( 500, 6000 )
self:SetDeployRadius( 500, 6000 )
self:SetPickupHeight( 0, 0 )
self:SetDeployHeight( 0, 0 )
self:SetShippingLane( ShippingLane )
self:SetMonitorTimeInterval( 600 )
return self
end
function AI_CARGO_DISPATCHER_SHIP:SetShippingLane( ShippingLane )
self.ShippingLane = ShippingLane
return self
end
function AI_CARGO_DISPATCHER_SHIP:AICargo( Ship, CargoSet )
return AI_CARGO_SHIP:New( Ship, CargoSet, 0, self.ShippingLane )
end

View File

@@ -1,661 +0,0 @@
--- **AI** - Models the intelligent transportation of cargo using helicopters.
--
-- ===
--
-- ### Author: **FlightControl**
--
-- ===
--
-- @module AI.AI_Cargo_Helicopter
-- @image AI_Cargo_Dispatching_For_Helicopters.JPG
-- @type AI_CARGO_HELICOPTER
-- @extends Core.Fsm#FSM_CONTROLLABLE
--- Brings a dynamic cargo handling capability for an AI helicopter group.
--
-- Helicopter carriers can be mobilized to intelligently transport infantry and other cargo within the simulation.
--
-- The AI_CARGO_HELICOPTER class uses the @{Cargo.Cargo} capabilities within the MOOSE framework.
-- @{Cargo.Cargo} must be declared within the mission to make the AI_CARGO_HELICOPTER object recognize the cargo.
-- Please consult the @{Cargo.Cargo} module for more information.
--
-- ## Cargo pickup.
--
-- Using the @{#AI_CARGO_HELICOPTER.Pickup}() method, you are able to direct the helicopters towards a point on the battlefield to board/load the cargo at the specific coordinate.
-- Ensure that the landing zone is horizontally flat, and that trees cannot be found in the landing vicinity, or the helicopters won't land or will even crash!
--
-- ## Cargo deployment.
--
-- Using the @{#AI_CARGO_HELICOPTER.Deploy}() method, you are able to direct the helicopters towards a point on the battlefield to unboard/unload the cargo at the specific coordinate.
-- Ensure that the landing zone is horizontally flat, and that trees cannot be found in the landing vicinity, or the helicopters won't land or will even crash!
--
-- ## Infantry health.
--
-- When infantry is unboarded from the helicopters, the infantry is actually respawned into the battlefield.
-- As a result, the unboarding infantry is very _healthy_ every time it unboards.
-- This is due to the limitation of the DCS simulator, which is not able to specify the health of new spawned units as a parameter.
-- However, infantry that was destroyed when unboarded, won't be respawned again. Destroyed is destroyed.
-- As a result, there is some additional strength that is gained when an unboarding action happens, but in terms of simulation balance this has
-- marginal impact on the overall battlefield simulation. Fortunately, the firing strength of infantry is limited, and thus, respacing healthy infantry every
-- time is not so much of an issue ...
--
--
-- # Developer Note
--
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
-- Therefore, this class is considered to be deprecated
--
-- ===
--
-- @field #AI_CARGO_HELICOPTER
AI_CARGO_HELICOPTER = {
ClassName = "AI_CARGO_HELICOPTER",
Coordinate = nil, -- Core.Point#COORDINATE,
}
AI_CARGO_QUEUE = {}
--- Creates a new AI_CARGO_HELICOPTER object.
-- @param #AI_CARGO_HELICOPTER self
-- @param Wrapper.Group#GROUP Helicopter
-- @param Core.Set#SET_CARGO CargoSet
-- @return #AI_CARGO_HELICOPTER
function AI_CARGO_HELICOPTER:New( Helicopter, CargoSet )
local self = BASE:Inherit( self, AI_CARGO:New( Helicopter, CargoSet ) ) -- #AI_CARGO_HELICOPTER
self.Zone = ZONE_GROUP:New( Helicopter:GetName(), Helicopter, 300 )
self:SetStartState( "Unloaded" )
-- Boarding
self:AddTransition( "Unloaded", "Pickup", "Unloaded" )
self:AddTransition( "*", "Landed", "*" )
self:AddTransition( "*", "Load", "*" )
self:AddTransition( "*", "Loaded", "Loaded" )
self:AddTransition( "Loaded", "PickedUp", "Loaded" )
-- Unboarding
self:AddTransition( "Loaded", "Deploy", "*" )
self:AddTransition( "*", "Queue", "*" )
self:AddTransition( "*", "Orbit" , "*" )
self:AddTransition( "*", "Destroyed", "*" )
self:AddTransition( "*", "Unload", "*" )
self:AddTransition( "*", "Unloaded", "Unloaded" )
self:AddTransition( "Unloaded", "Deployed", "Unloaded" )
-- RTB
self:AddTransition( "*", "Home" , "*" )
--- Pickup Handler OnBefore for AI_CARGO_HELICOPTER
-- @function [parent=#AI_CARGO_HELICOPTER] OnBeforePickup
-- @param #AI_CARGO_HELICOPTER self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param Core.Point#COORDINATE Coordinate
-- @return #boolean
--- Pickup Handler OnAfter for AI_CARGO_HELICOPTER
-- @function [parent=#AI_CARGO_HELICOPTER] OnAfterPickup
-- @param #AI_CARGO_HELICOPTER self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param Core.Point#COORDINATE Coordinate
-- @param #number Speed Speed in km/h to fly to the pickup coordinate. Default is 50% of max possible speed the unit can go.
--- PickedUp Handler OnAfter for AI_CARGO_HELICOPTER - Cargo set has been picked up, ready to deploy
-- @function [parent=#AI_CARGO_HELICOPTER] OnAfterPickedUp
-- @param #AI_CARGO_HELICOPTER self
-- @param Wrapper.Group#GROUP Helicopter The helicopter #GROUP object
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param Wrapper.Unit#UNIT Unit The helicopter #UNIT object
--- Unloaded Handler OnAfter for AI_CARGO_HELICOPTER - Cargo unloaded, carrier is empty
-- @function [parent=#AI_CARGO_HELICOPTER] OnAfterUnloaded
-- @param #AI_CARGO_HELICOPTER self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param Cargo.CargoGroup#CARGO_GROUP Cargo The #CARGO_GROUP object.
-- @param Wrapper.Unit#UNIT Unit The helicopter #UNIT object
--- Pickup Trigger for AI_CARGO_HELICOPTER
-- @function [parent=#AI_CARGO_HELICOPTER] Pickup
-- @param #AI_CARGO_HELICOPTER self
-- @param Core.Point#COORDINATE Coordinate
-- @param #number Speed Speed in km/h to fly to the pickup coordinate. Default is 50% of max possible speed the unit can go.
--- Pickup Asynchronous Trigger for AI_CARGO_HELICOPTER
-- @function [parent=#AI_CARGO_HELICOPTER] __Pickup
-- @param #AI_CARGO_HELICOPTER self
-- @param #number Delay Delay in seconds.
-- @param Core.Point#COORDINATE Coordinate
-- @param #number Speed Speed in km/h to go to the pickup coordinate. Default is 50% of max possible speed the unit can go.
--- Deploy Handler OnBefore for AI_CARGO_HELICOPTER
-- @function [parent=#AI_CARGO_HELICOPTER] OnBeforeDeploy
-- @param #AI_CARGO_HELICOPTER self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param Core.Point#COORDINATE Coordinate Place at which cargo is deployed.
-- @param #number Speed Speed in km/h to fly to the pickup coordinate. Default is 50% of max possible speed the unit can go.
-- @return #boolean
--- Deploy Handler OnAfter for AI_CARGO_HELICOPTER
-- @function [parent=#AI_CARGO_HELICOPTER] OnAfterDeploy
-- @param #AI_CARGO_HELICOPTER self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param Core.Point#COORDINATE Coordinate
-- @param #number Speed Speed in km/h to fly to the pickup coordinate. Default is 50% of max possible speed the unit can go.
--- Deployed Handler OnAfter for AI_CARGO_HELICOPTER
-- @function [parent=#AI_CARGO_HELICOPTER] OnAfterDeployed
-- @param #AI_CARGO_HELICOPTER self
-- @param #string From
-- @param #string Event
-- @param #string To
--- Deploy Trigger for AI_CARGO_HELICOPTER
-- @function [parent=#AI_CARGO_HELICOPTER] Deploy
-- @param #AI_CARGO_HELICOPTER self
-- @param Core.Point#COORDINATE Coordinate Place at which the cargo is deployed.
-- @param #number Speed Speed in km/h to fly to the pickup coordinate. Default is 50% of max possible speed the unit can go.
--- Deploy Asynchronous Trigger for AI_CARGO_HELICOPTER
-- @function [parent=#AI_CARGO_HELICOPTER] __Deploy
-- @param #number Delay Delay in seconds.
-- @param #AI_CARGO_HELICOPTER self
-- @param Core.Point#COORDINATE Coordinate Place at which the cargo is deployed.
-- @param #number Speed Speed in km/h to fly to the pickup coordinate. Default is 50% of max possible speed the unit can go.
--- Home Trigger for AI_CARGO_HELICOPTER
-- @function [parent=#AI_CARGO_HELICOPTER] Home
-- @param #AI_CARGO_HELICOPTER self
-- @param Core.Point#COORDINATE Coordinate Place to which the helicopter will go.
-- @param #number Speed (optional) Speed in km/h to fly to the pickup coordinate. Default is 50% of max possible speed the unit can go.
-- @param #number Height (optional) Height the Helicopter should be flying at.
--- Home Asynchronous Trigger for AI_CARGO_HELICOPTER
-- @function [parent=#AI_CARGO_HELICOPTER] __Home
-- @param #number Delay Delay in seconds.
-- @param #AI_CARGO_HELICOPTER self
-- @param Core.Point#COORDINATE Coordinate Place to which the helicopter will go.
-- @param #number Speed (optional) Speed in km/h to fly to the pickup coordinate. Default is 50% of max possible speed the unit can go.
-- @param #number Height (optional) Height the Helicopter should be flying at.
-- We need to capture the Crash events for the helicopters.
-- The helicopter reference is used in the semaphore AI_CARGO_QUEUE.
-- So, we need to unlock this when the helo is not anymore ...
Helicopter:HandleEvent( EVENTS.Crash,
function( Helicopter, EventData )
AI_CARGO_QUEUE[Helicopter] = nil
end
)
-- We need to capture the Land events for the helicopters.
-- The helicopter reference is used in the semaphore AI_CARGO_QUEUE.
-- So, we need to unlock this when the helo has landed, which can be anywhere ...
-- But only free the landing coordinate after 1 minute, to ensure that all helos have left.
Helicopter:HandleEvent( EVENTS.Land,
function( Helicopter, EventData )
self:ScheduleOnce( 60,
function( Helicopter )
AI_CARGO_QUEUE[Helicopter] = nil
end, Helicopter
)
end
)
self:SetCarrier( Helicopter )
self.landingspeed = 15 -- kph
self.landingheight = 5.5 -- meter
return self
end
--- Set the Carrier.
-- @param #AI_CARGO_HELICOPTER self
-- @param Wrapper.Group#GROUP Helicopter
-- @return #AI_CARGO_HELICOPTER
function AI_CARGO_HELICOPTER:SetCarrier( Helicopter )
local AICargo = self
self.Helicopter = Helicopter -- Wrapper.Group#GROUP
self.Helicopter:SetState( self.Helicopter, "AI_CARGO_HELICOPTER", self )
self.RoutePickup = false
self.RouteDeploy = false
Helicopter:HandleEvent( EVENTS.Dead )
Helicopter:HandleEvent( EVENTS.Hit )
Helicopter:HandleEvent( EVENTS.Land )
function Helicopter:OnEventDead( EventData )
local AICargoTroops = self:GetState( self, "AI_CARGO_HELICOPTER" )
self:F({AICargoTroops=AICargoTroops})
if AICargoTroops then
self:F({})
if not AICargoTroops:Is( "Loaded" ) then
-- There are enemies within combat range. Unload the Helicopter.
AICargoTroops:Destroyed()
end
end
end
function Helicopter:OnEventLand( EventData )
AICargo:Landed()
end
self.Coalition = self.Helicopter:GetCoalition()
self:SetControllable( Helicopter )
return self
end
--- Set landingspeed and -height for helicopter landings. Adjust after tracing if your helis get stuck after landing.
-- @param #AI_CARGO_HELICOPTER self
-- @param #number speed Landing speed in kph(!), e.g. 15
-- @param #number height Landing height in meters(!), e.g. 5.5
-- @return #AI_CARGO_HELICOPTER self
-- @usage If your choppers get stuck, add tracing to your script to determine if they hit the right parameters like so:
--
-- BASE:TraceOn()
-- BASE:TraceClass("AI_CARGO_HELICOPTER")
--
-- Watch the DCS.log for entries stating `Helicopter:<name>, Height = Helicopter:<number>, Velocity = Helicopter:<number>`
-- Adjust if necessary.
function AI_CARGO_HELICOPTER:SetLandingSpeedAndHeight(speed, height)
local _speed = speed or 15
local _height = height or 5.5
self.landingheight = _height
self.landingspeed = _speed
return self
end
-- @param #AI_CARGO_HELICOPTER self
-- @param Wrapper.Group#GROUP Helicopter
-- @param From
-- @param Event
-- @param To
function AI_CARGO_HELICOPTER:onafterLanded( Helicopter, From, Event, To )
self:F({From, Event, To})
Helicopter:F( { Name = Helicopter:GetName() } )
if Helicopter and Helicopter:IsAlive() then
-- S_EVENT_LAND is directly called in two situations:
-- 1 - When the helo lands normally on the ground.
-- 2 - when the helo is hit and goes RTB or even when it is destroyed.
-- For point 2, this is an issue, the infantry may not unload in this case!
-- So we check if the helo is on the ground, and velocity< 15.
-- Only then the infantry can unload (and load too, for consistency)!
self:T( { Helicopter:GetName(), Height = Helicopter:GetHeight( true ), Velocity = Helicopter:GetVelocityKMH() } )
if self.RoutePickup == true then
if Helicopter:GetHeight( true ) <= self.landingheight then --and Helicopter:GetVelocityKMH() < self.landingspeed then
--self:Load( Helicopter:GetPointVec2() )
self:Load( self.PickupZone )
self.RoutePickup = false
end
end
if self.RouteDeploy == true then
if Helicopter:GetHeight( true ) <= self.landingheight then --and Helicopter:GetVelocityKMH() < self.landingspeed then
self:Unload( self.DeployZone )
self.RouteDeploy = false
end
end
end
end
-- @param #AI_CARGO_HELICOPTER self
-- @param Wrapper.Group#GROUP Helicopter
-- @param From
-- @param Event
-- @param To
-- @param Core.Point#COORDINATE Coordinate
-- @param #number Speed
function AI_CARGO_HELICOPTER:onafterQueue( Helicopter, From, Event, To, Coordinate, Speed, DeployZone )
self:F({From, Event, To, Coordinate, Speed, DeployZone})
local HelicopterInZone = false
if Helicopter and Helicopter:IsAlive() == true then
local Distance = Coordinate:DistanceFromPointVec2( Helicopter:GetCoordinate() )
if Distance > 2000 then
self:__Queue( -10, Coordinate, Speed, DeployZone )
else
local ZoneFree = true
for Helicopter, ZoneQueue in pairs( AI_CARGO_QUEUE ) do
local ZoneQueue = ZoneQueue -- Core.Zone#ZONE_RADIUS
if ZoneQueue:IsCoordinateInZone( Coordinate ) then
ZoneFree = false
end
end
self:F({ZoneFree=ZoneFree})
if ZoneFree == true then
local ZoneQueue = ZONE_RADIUS:New( Helicopter:GetName(), Coordinate:GetVec2(), 100 )
AI_CARGO_QUEUE[Helicopter] = ZoneQueue
local Route = {}
-- local CoordinateFrom = Helicopter:GetCoordinate()
-- local WaypointFrom = CoordinateFrom:WaypointAir(
-- "RADIO",
-- POINT_VEC3.RoutePointType.TurningPoint,
-- POINT_VEC3.RoutePointAction.TurningPoint,
-- Speed,
-- true
-- )
-- Route[#Route+1] = WaypointFrom
local CoordinateTo = Coordinate
local landheight = CoordinateTo:GetLandHeight() -- get target height
CoordinateTo.y = landheight + 50 -- flight height should be 50m above ground
local WaypointTo = CoordinateTo:WaypointAir(
"RADIO",
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
50,
true
)
Route[#Route+1] = WaypointTo
local Tasks = {}
Tasks[#Tasks+1] = Helicopter:TaskLandAtVec2( CoordinateTo:GetVec2() )
Route[#Route].task = Helicopter:TaskCombo( Tasks )
Route[#Route+1] = WaypointTo
-- Now route the helicopter
Helicopter:Route( Route, 0 )
-- Keep the DeployZone, because when the helo has landed, we want to provide the DeployZone to the mission designer as part of the Unloaded event.
self.DeployZone = DeployZone
else
self:__Queue( -10, Coordinate, Speed, DeployZone )
end
end
else
AI_CARGO_QUEUE[Helicopter] = nil
end
end
-- @param #AI_CARGO_HELICOPTER self
-- @param Wrapper.Group#GROUP Helicopter
-- @param From
-- @param Event
-- @param To
-- @param Core.Point#COORDINATE Coordinate
-- @param #number Speed
function AI_CARGO_HELICOPTER:onafterOrbit( Helicopter, From, Event, To, Coordinate )
self:F({From, Event, To, Coordinate})
if Helicopter and Helicopter:IsAlive() then
local Route = {}
local CoordinateTo = Coordinate
local landheight = CoordinateTo:GetLandHeight() -- get target height
CoordinateTo.y = landheight + 50 -- flight height should be 50m above ground
local WaypointTo = CoordinateTo:WaypointAir("RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, 50, true)
Route[#Route+1] = WaypointTo
local Tasks = {}
Tasks[#Tasks+1] = Helicopter:TaskOrbitCircle( math.random( 30, 80 ), 150, CoordinateTo:GetRandomCoordinateInRadius( 800, 500 ) )
Route[#Route].task = Helicopter:TaskCombo( Tasks )
Route[#Route+1] = WaypointTo
-- Now route the helicopter
Helicopter:Route(Route, 0)
end
end
--- On after Deployed event.
-- @param #AI_CARGO_HELICOPTER self
-- @param Wrapper.Group#GROUP Helicopter
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Cargo.Cargo#CARGO Cargo Cargo object.
-- @param #boolean Deployed Cargo is deployed.
-- @return #boolean True if all cargo has been unloaded.
function AI_CARGO_HELICOPTER:onafterDeployed( Helicopter, From, Event, To, DeployZone )
self:F( { From, Event, To, DeployZone = DeployZone } )
self:Orbit( Helicopter:GetCoordinate(), 50 )
-- Free the coordinate zone after 30 seconds, so that the original helicopter can fly away first.
self:ScheduleOnce( 30,
function( Helicopter )
AI_CARGO_QUEUE[Helicopter] = nil
end, Helicopter
)
self:GetParent( self, AI_CARGO_HELICOPTER ).onafterDeployed( self, Helicopter, From, Event, To, DeployZone )
end
--- On after Pickup event.
-- @param #AI_CARGO_HELICOPTER self
-- @param Wrapper.Group#GROUP Helicopter
-- @param From
-- @param Event
-- @param To
-- @param Core.Point#COORDINATE Coordinate Pickup place.
-- @param #number Speed Speed in km/h to fly to the pickup coordinate. Default is 50% of max possible speed the unit can go.
-- @param #number Height Height in meters to move to the pickup coordinate. This parameter is ignored for APCs.
-- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided.
function AI_CARGO_HELICOPTER:onafterPickup( Helicopter, From, Event, To, Coordinate, Speed, Height, PickupZone )
self:F({Coordinate, Speed, Height, PickupZone })
if Helicopter and Helicopter:IsAlive() ~= nil then
Helicopter:Activate()
self.RoutePickup = true
Coordinate.y = Height
local _speed=Speed or Helicopter:GetSpeedMax()*0.5
local Route = {}
--- Calculate the target route point.
local CoordinateFrom = Helicopter:GetCoordinate()
--- Create a route point of type air.
local WaypointFrom = CoordinateFrom:WaypointAir("RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, _speed, true)
--- Create a route point of type air.
local CoordinateTo = Coordinate
local landheight = CoordinateTo:GetLandHeight() -- get target height
CoordinateTo.y = landheight + 50 -- flight height should be 50m above ground
local WaypointTo = CoordinateTo:WaypointAir("RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint,_speed, true)
Route[#Route+1] = WaypointFrom
Route[#Route+1] = WaypointTo
--- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable...
Helicopter:WayPointInitialize( Route )
local Tasks = {}
Tasks[#Tasks+1] = Helicopter:TaskLandAtVec2( CoordinateTo:GetVec2() )
Route[#Route].task = Helicopter:TaskCombo( Tasks )
Route[#Route+1] = WaypointTo
-- Now route the helicopter
Helicopter:Route( Route, 1 )
self.PickupZone = PickupZone
self:GetParent( self, AI_CARGO_HELICOPTER ).onafterPickup( self, Helicopter, From, Event, To, Coordinate, Speed, Height, PickupZone )
end
end
--- Depoloy function and queue.
-- @param #AI_CARGO_HELICOPTER self
-- @param Wrapper.Group#GROUP AICargoHelicopter
-- @param Core.Point#COORDINATE Coordinate Coordinate
function AI_CARGO_HELICOPTER:_Deploy( AICargoHelicopter, Coordinate, DeployZone )
AICargoHelicopter:__Queue( -10, Coordinate, 100, DeployZone )
end
--- On after Deploy event.
-- @param #AI_CARGO_HELICOPTER self
-- @param Wrapper.Group#GROUP Helicopter Transport helicopter.
-- @param From
-- @param Event
-- @param To
-- @param Core.Point#COORDINATE Coordinate Place at which the cargo is deployed.
-- @param #number Speed Speed in km/h to fly to the pickup coordinate. Default is 50% of max possible speed the unit can go.
-- @param #number Height Height in meters to move to the deploy coordinate.
function AI_CARGO_HELICOPTER:onafterDeploy( Helicopter, From, Event, To, Coordinate, Speed, Height, DeployZone )
self:F({From, Event, To, Coordinate, Speed, Height, DeployZone})
if Helicopter and Helicopter:IsAlive() ~= nil then
self.RouteDeploy = true
local Route = {}
--- Calculate the target route point.
Coordinate.y = Height
local _speed=Speed or Helicopter:GetSpeedMax()*0.5
--- Create a route point of type air.
local CoordinateFrom = Helicopter:GetCoordinate()
local WaypointFrom = CoordinateFrom:WaypointAir("RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, _speed, true)
Route[#Route+1] = WaypointFrom
Route[#Route+1] = WaypointFrom
--- Create a route point of type air.
local CoordinateTo = Coordinate
local landheight = CoordinateTo:GetLandHeight() -- get target height
CoordinateTo.y = landheight + 50 -- flight height should be 50m above ground
local WaypointTo = CoordinateTo:WaypointAir("RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, _speed, true)
Route[#Route+1] = WaypointTo
Route[#Route+1] = WaypointTo
--- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable...
Helicopter:WayPointInitialize( Route )
local Tasks = {}
-- The _Deploy function does not exist.
Tasks[#Tasks+1] = Helicopter:TaskFunction( "AI_CARGO_HELICOPTER._Deploy", self, Coordinate, DeployZone )
Tasks[#Tasks+1] = Helicopter:TaskOrbitCircle( math.random( 30, 100 ), _speed, CoordinateTo:GetRandomCoordinateInRadius( 800, 500 ) )
--Tasks[#Tasks+1] = Helicopter:TaskLandAtVec2( CoordinateTo:GetVec2() )
Route[#Route].task = Helicopter:TaskCombo( Tasks )
Route[#Route+1] = WaypointTo
-- Now route the helicopter
Helicopter:Route( Route, 0 )
self:GetParent( self, AI_CARGO_HELICOPTER ).onafterDeploy( self, Helicopter, From, Event, To, Coordinate, Speed, Height, DeployZone )
end
end
--- On after Home event.
-- @param #AI_CARGO_HELICOPTER self
-- @param Wrapper.Group#GROUP Helicopter
-- @param From
-- @param Event
-- @param To
-- @param Core.Point#COORDINATE Coordinate Home place.
-- @param #number Speed Speed in km/h to fly to the pickup coordinate. Default is 50% of max possible speed the unit can go.
-- @param #number Height Height in meters to move to the home coordinate.
-- @param Core.Zone#ZONE HomeZone The zone wherein the carrier will return when all cargo has been transported. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE.
function AI_CARGO_HELICOPTER:onafterHome( Helicopter, From, Event, To, Coordinate, Speed, Height, HomeZone )
self:F({From, Event, To, Coordinate, Speed, Height})
if Helicopter and Helicopter:IsAlive() ~= nil then
self.RouteHome = true
local Route = {}
--- Calculate the target route point.
--Coordinate.y = Height
Height = Height or 50
Speed = Speed or Helicopter:GetSpeedMax()*0.5
--- Create a route point of type air.
local CoordinateFrom = Helicopter:GetCoordinate()
local WaypointFrom = CoordinateFrom:WaypointAir("RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, Speed, true)
Route[#Route+1] = WaypointFrom
--- Create a route point of type air.
local CoordinateTo = Coordinate
local landheight = CoordinateTo:GetLandHeight() -- get target height
CoordinateTo.y = landheight + Height -- flight height should be 50m above ground
local WaypointTo = CoordinateTo:WaypointAir("RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, Speed, true)
Route[#Route+1] = WaypointTo
--- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable...
Helicopter:WayPointInitialize( Route )
local Tasks = {}
Tasks[#Tasks+1] = Helicopter:TaskLandAtVec2( CoordinateTo:GetVec2() )
Route[#Route].task = Helicopter:TaskCombo( Tasks )
Route[#Route+1] = WaypointTo
-- Now route the helicopter
Helicopter:Route(Route, 0)
end
end

View File

@@ -1,402 +0,0 @@
--- **AI** - Models the intelligent transportation of infantry and other cargo.
--
-- ===
--
-- ### Author: **acrojason** (derived from AI_Cargo_APC by FlightControl)
--
-- ===
--
-- @module AI.AI_Cargo_Ship
-- @image AI_Cargo_Dispatcher.JPG
-- @type AI_CARGO_SHIP
-- @extends AI.AI_Cargo#AI_CARGO
--- Brings a dynamic cargo handling capability for an AI naval group.
--
-- Naval ships can be utilized to transport cargo around the map following naval shipping lanes.
-- The AI_CARGO_SHIP class uses the @{Cargo.Cargo} capabilities within the MOOSE framework.
-- @{Cargo.Cargo} must be declared within the mission or warehouse to make the AI_CARGO_SHIP recognize the cargo.
-- Please consult the @{Cargo.Cargo} module for more information.
--
-- ## Cargo loading.
--
-- The module will automatically load cargo when the Ship is within boarding or loading radius.
-- The boarding or loading radius is specified when the cargo is created in the simulation and depends on the type of
-- cargo and the specified boarding radius.
--
-- ## Defending the Ship when enemies are nearby
-- This is not supported for naval cargo because most tanks don't float. Protect your transports...
--
-- ## Infantry or cargo **health**.
-- When cargo is unboarded from the Ship, the cargo is actually respawned into the battlefield.
-- As a result, the unboarding cargo is very _healthy_ every time it unboards.
-- This is due to the limitation of the DCS simulator, which is not able to specify the health of newly spawned units as a parameter.
-- However, cargo that was destroyed when unboarded and following the Ship won't be respawned again (this is likely not a thing for
-- naval cargo due to the lack of support for defending the Ship mentioned above). Destroyed is destroyed.
-- As a result, there is some additional strength that is gained when an unboarding action happens, but in terms of simulation balance
-- this has marginal impact on the overall battlefield simulation. Given the relatively short duration of DCS missions and the somewhat
-- lengthy naval transport times, most units entering the Ship as cargo will be freshly en route to an amphibious landing or transporting
-- between warehouses.
--
-- ## Control the Ships on the map.
--
-- Currently, naval transports can only be controlled via scripts due to their reliance upon predefined Shipping Lanes created in the Mission
-- Editor. An interesting future enhancement could leverage new pathfinding functionality for ships in the Ops module.
--
-- ## Cargo deployment.
--
-- Using the @{#AI_CARGO_SHIP.Deploy}() method, you are able to direct the Ship towards a Deploy zone to unboard/unload the cargo at the
-- specified coordinate. The Ship will follow the Shipping Lane to ensure consistent cargo transportation within the simulation environment.
--
-- ## Cargo pickup.
--
-- Using the @{#AI_CARGO_SHIP.Pickup}() method, you are able to direct the Ship towards a Pickup zone to board/load the cargo at the specified
-- coordinate. The Ship will follow the Shipping Lane to ensure consistent cargo transportation within the simulation environment.
--
--
-- # Developer Note
--
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
-- Therefore, this class is considered to be deprecated
--
-- @field #AI_CARGO_SHIP
AI_CARGO_SHIP = {
ClassName = "AI_CARGO_SHIP",
Coordinate = nil -- Core.Point#COORDINATE
}
--- Creates a new AI_CARGO_SHIP object.
-- @param #AI_CARGO_SHIP self
-- @param Wrapper.Group#GROUP Ship The carrier Ship group
-- @param Core.Set#SET_CARGO CargoSet The set of cargo to be transported
-- @param #number CombatRadius Provide the combat radius to defend the carrier by unboarding the cargo when enemies are nearby. When CombatRadius is 0, no defense will occur.
-- @param #table ShippingLane Table containing list of Shipping Lanes to be used
-- @return #AI_CARGO_SHIP
function AI_CARGO_SHIP:New( Ship, CargoSet, CombatRadius, ShippingLane )
local self = BASE:Inherit( self, AI_CARGO:New( Ship, CargoSet ) ) -- #AI_CARGO_SHIP
self:AddTransition( "*", "Monitor", "*" )
self:AddTransition( "*", "Destroyed", "Destroyed" )
self:AddTransition( "*", "Home", "*" )
self:SetCombatRadius( 0 ) -- Don't want to deploy cargo in middle of water to defend Ship, so set CombatRadius to 0
self:SetShippingLane ( ShippingLane )
self:SetCarrier( Ship )
return self
end
--- Set the Carrier
-- @param #AI_CARGO_SHIP self
-- @param Wrapper.Group#GROUP CargoCarrier
-- @return #AI_CARGO_SHIP
function AI_CARGO_SHIP:SetCarrier( CargoCarrier )
self.CargoCarrier = CargoCarrier -- Wrapper.Group#GROUIP
self.CargoCarrier:SetState( self.CargoCarrier, "AI_CARGO_SHIP", self )
CargoCarrier:HandleEvent( EVENTS.Dead )
function CargoCarrier:OnEventDead( EventData )
self:F({"dead"})
local AICargoTroops = self:GetState( self, "AI_CARGO_SHIP" )
self:F({AICargoTroops=AICargoTroops})
if AICargoTroops then
self:F({})
if not AICargoTroops:Is( "Loaded" ) then
-- Better hope they can swim!
AICargoTroops:Destroyed()
end
end
end
self.Zone = ZONE_UNIT:New( self.CargoCarrier:GetName() .. "-Zone", self.CargoCarrier, self.CombatRadius )
self.Coalition = self.CargoCarrier:GetCoalition()
self:SetControllable( CargoCarrier )
return self
end
--- FInd a free Carrier within a radius
-- @param #AI_CARGO_SHIP self
-- @param Core.Point#COORDINATE Coordinate
-- @param #number Radius
-- @return Wrapper.Group#GROUP NewCarrier
function AI_CARGO_SHIP:FindCarrier( Coordinate, Radius )
local CoordinateZone = ZONE_RADIUS:New( "Zone", Coordinate:GetVec2(), Radius )
CoordinateZone:Scan( { Object.Category.UNIT } )
for _, DCSUnit in pairs( CoordinateZone:GetScannedUnits() ) do
local NearUnit = UNIT:Find( DCSUnit )
self:F({NearUnit=NearUnit})
if not NearUnit:GetState( NearUnit, "AI_CARGO_SHIP" ) then
local Attributes = NearUnit:GetDesc()
self:F({Desc=Attributes})
if NearUnit:HasAttributes( "Trucks" ) then
return NearUnit:GetGroup()
end
end
end
return nil
end
function AI_CARGO_SHIP:SetShippingLane( ShippingLane )
self.ShippingLane = ShippingLane
return self
end
function AI_CARGO_SHIP:SetCombatRadius( CombatRadius )
self.CombatRadius = CombatRadius or 0
return self
end
--- Follow Infantry to the Carrier
-- @param #AI_CARGO_SHIP self
-- @param #AI_CARGO_SHIP Me
-- @param Wrapper.Unit#UNIT ShipUnit
-- @param Cargo.CargoGroup#CARGO_GROUP Cargo
-- @return #AI_CARGO_SHIP
function AI_CARGO_SHIP:FollowToCarrier( Me, ShipUnit, CargoGroup )
local InfantryGroup = CargoGroup:GetGroup()
self:F( { self=self:GetClassNameAndID(), InfantryGroup = InfantryGroup:GetName() } )
if ShipUnit:IsAlive() then
-- Check if the Cargo is near the CargoCarrier
if InfantryGroup:IsPartlyInZone( ZONE_UNIT:New( "Radius", ShipUnit, 1000 ) ) then
-- Cargo does not need to navigate to Carrier
Me:Guard()
else
self:F( { InfantryGroup = InfantryGroup:GetName() } )
if InfantryGroup:IsAlive() then
self:F( { InfantryGroup = InfantryGroup:GetName() } )
local Waypoints = {}
-- Calculate new route
local FromCoord = InfantryGroup:GetCoordinate()
local FromGround = FromCoord:WaypointGround( 10, "Diamond" )
self:F({FromGround=FromGround})
table.insert( Waypoints, FromGround )
local ToCoord = ShipUnit:GetCoordinate():GetRandomCoordinateInRadius( 10, 5 )
local ToGround = ToCoord:WaypointGround( 10, "Diamond" )
self:F({ToGround=ToGround})
table.insert( Waypoints, ToGround )
local TaskRoute = InfantryGroup:TaskFunction( "AI_CARGO_SHIP.FollowToCarrier", Me, ShipUnit, CargoGroup )
self:F({Waypoints=Waypoints})
local Waypoint = Waypoints[#Waypoints]
InfantryGroup:SetTaskWaypoint( Waypoint, TaskRoute ) -- Set for the given Route at Waypoint 2 the TaskRouteToZone
InfantryGroup:Route( Waypoints, 1 ) -- Move after a random number of seconds to the Route. See Route method for details
end
end
end
end
function AI_CARGO_SHIP:onafterMonitor( Ship, From, Event, To )
self:F( { Ship, From, Event, To, IsTransporting = self:IsTransporting() } )
if self.CombatRadius > 0 then
-- We really shouldn't find ourselves in here for Ships since the CombatRadius should always be 0.
-- This is to avoid Unloading the Ship in the middle of the sea.
if Ship and Ship:IsAlive() then
if self.CarrierCoordinate then
if self:IsTransporting() == true then
local Coordinate = Ship:GetCoordinate()
if self:Is( "Unloaded" ) or self:Is( "Loaded" ) then
self.Zone:Scan( { Object.Category.UNIT } )
if self.Zone:IsAllInZoneOfCoalition( self.Coalition ) then
if self:Is( "Unloaded" ) then
-- There are no enemies within combat radius. Reload the CargoCarrier.
self:Reload()
end
else
if self:Is( "Loaded" ) then
-- There are enemies within combat radius. Unload the CargoCarrier.
self:__Unload( 1, nil, true ) -- The 2nd parameter is true, which means that the unload is for defending the carrier, not to deploy!
else
if self:Is( "Unloaded" ) then
--self:Follow()
end
self:F( "I am here" .. self:GetCurrentState() )
if self:Is( "Following" ) then
for Cargo, ShipUnit in pairs( self.Carrier_Cargo ) do
local Cargo = Cargo -- Cargo.Cargo#CARGO
local ShipUnit = ShipUnit -- Wrapper.Unit#UNIT
if Cargo:IsAlive() then
if not Cargo:IsNear( ShipUnit, 40 ) then
ShipUnit:RouteStop()
self.CarrierStopped = true
else
if self.CarrierStopped then
if Cargo:IsNear( ShipUnit, 25 ) then
ShipUnit:RouteResume()
self.CarrierStopped = nil
end
end
end
end
end
end
end
end
end
end
end
self.CarrierCoordinate = Ship:GetCoordinate()
end
self:__Monitor( -5 )
end
end
--- Check if cargo ship is alive and trigger Load event
-- @param Wrapper.Group#Group Ship
-- @param #AI_CARGO_SHIP self
function AI_CARGO_SHIP._Pickup( Ship, self, Coordinate, Speed, PickupZone )
Ship:F( { "AI_CARGO_Ship._Pickup:", Ship:GetName() } )
if Ship:IsAlive() then
self:Load( PickupZone )
end
end
--- Check if cargo ship is alive and trigger Unload event. Good time to remind people that Lua is case sensitive and Unload != UnLoad
-- @param Wrapper.Group#GROUP Ship
-- @param #AI_CARGO_SHIP self
function AI_CARGO_SHIP._Deploy( Ship, self, Coordinate, DeployZone )
Ship:F( { "AI_CARGO_Ship._Deploy:", Ship } )
if Ship:IsAlive() then
self:Unload( DeployZone )
end
end
--- on after Pickup event.
-- @param AI_CARGO_SHIP Ship
-- @param From
-- @param Event
-- @param To
-- @param Core.Point#COORDINATE Coordinate of the pickup point
-- @param #number Speed Speed in km/h to sail to the pickup coordinate. Default is 50% of max speed for the unit
-- @param #number Height Altitude in meters to move to the pickup coordinate. This parameter is ignored for Ships
-- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil if there was no PickupZoneSet provided
function AI_CARGO_SHIP:onafterPickup( Ship, From, Event, To, Coordinate, Speed, Height, PickupZone )
if Ship and Ship:IsAlive() then
AI_CARGO_SHIP._Pickup( Ship, self, Coordinate, Speed, PickupZone )
self:GetParent( self, AI_CARGO_SHIP ).onafterPickup( self, Ship, From, Event, To, Coordinate, Speed, Height, PickupZone )
end
end
--- On after Deploy event.
-- @param #AI_CARGO_SHIP self
-- @param Wrapper.Group#GROUP SHIP
-- @param From
-- @param Event
-- @param To
-- @param Core.Point#COORDINATE Coordinate Coordinate of the deploy point
-- @param #number Speed Speed in km/h to sail to the deploy coordinate. Default is 50% of max speed for the unit
-- @param #number Height Altitude in meters to move to the deploy coordinate. This parameter is ignored for Ships
-- @param Core.Zone#ZONE DeployZone The zone where the cargo will be deployed.
function AI_CARGO_SHIP:onafterDeploy( Ship, From, Event, To, Coordinate, Speed, Height, DeployZone )
if Ship and Ship:IsAlive() then
Speed = Speed or Ship:GetSpeedMax()*0.8
local lane = self.ShippingLane
if lane then
local Waypoints = {}
for i=1, #lane do
local coord = lane[i]
local Waypoint = coord:WaypointGround(_speed)
table.insert(Waypoints, Waypoint)
end
local TaskFunction = Ship:TaskFunction( "AI_CARGO_SHIP._Deploy", self, Coordinate, DeployZone )
local Waypoint = Waypoints[#Waypoints]
Ship:SetTaskWaypoint( Waypoint, TaskFunction )
Ship:Route(Waypoints, 1)
self:GetParent( self, AI_CARGO_SHIP ).onafterDeploy( self, Ship, From, Event, To, Coordinate, Speed, Height, DeployZone )
else
self:E(self.lid.."ERROR: No shipping lane defined for Naval Transport!")
end
end
end
--- On after Unload event.
-- @param #AI_CARGO_SHIP self
-- @param Wrapper.Group#GROUP Ship
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE.
function AI_CARGO_SHIP:onafterUnload( Ship, From, Event, To, DeployZone, Defend )
self:F( { Ship, From, Event, To, DeployZone, Defend = Defend } )
local UnboardInterval = 5
local UnboardDelay = 5
if Ship and Ship:IsAlive() then
for _, ShipUnit in pairs( Ship:GetUnits() ) do
local ShipUnit = ShipUnit -- Wrapper.Unit#UNIT
Ship:RouteStop()
for _, Cargo in pairs( ShipUnit:GetCargo() ) do
self:F( { Cargo = Cargo:GetName(), Isloaded = Cargo:IsLoaded() } )
if Cargo:IsLoaded() then
local unboardCoord = DeployZone:GetRandomPointVec2()
Cargo:__UnBoard( UnboardDelay, unboardCoord, 1000)
UnboardDelay = UnboardDelay + Cargo:GetCount() * UnboardInterval
self:__Unboard( UnboardDelay, Cargo, ShipUnit, DeployZone, Defend )
if not Defend == true then
Cargo:SetDeployed( true )
end
end
end
end
end
end
function AI_CARGO_SHIP:onafterHome( Ship, From, Event, To, Coordinate, Speed, Height, HomeZone )
if Ship and Ship:IsAlive() then
self.RouteHome = true
Speed = Speed or Ship:GetSpeedMax()*0.8
local lane = self.ShippingLane
if lane then
local Waypoints = {}
-- Need to find a more generalized way to do this instead of reversing the shipping lane.
-- This only works if the Source/Dest route waypoints are numbered 1..n and not n..1
for i=#lane, 1, -1 do
local coord = lane[i]
local Waypoint = coord:WaypointGround(_speed)
table.insert(Waypoints, Waypoint)
end
local Waypoint = Waypoints[#Waypoints]
Ship:Route(Waypoints, 1)
else
self:E(self.lid.."ERROR: No shipping lane defined for Naval Transport!")
end
end
end

File diff suppressed because it is too large Load Diff

View File

@@ -1,190 +0,0 @@
--- **AI** - Models the automatic assignment of AI escorts to player flights.
--
-- ## Features:
-- --
-- * Provides the facilities to trigger escorts when players join flight slots.
-- *
--
-- ===
--
-- ### Author: **FlightControl**
--
-- ===
--
-- @module AI.AI_Escort_Dispatcher
-- @image MOOSE.JPG
-- @type AI_ESCORT_DISPATCHER
-- @extends Core.Fsm#FSM
--- Models the automatic assignment of AI escorts to player flights.
--
-- # Developer Note
--
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
-- Therefore, this class is considered to be deprecated
--
-- ===
--
-- @field #AI_ESCORT_DISPATCHER
AI_ESCORT_DISPATCHER = {
ClassName = "AI_ESCORT_DISPATCHER",
}
-- @field #list
AI_ESCORT_DISPATCHER.AI_Escorts = {}
--- Creates a new AI_ESCORT_DISPATCHER object.
-- @param #AI_ESCORT_DISPATCHER self
-- @param Core.Set#SET_GROUP CarrierSet The set of @{Wrapper.Group#GROUP} objects of carriers for which escorts are spawned in.
-- @param Core.Spawn#SPAWN EscortSpawn The spawn object that will spawn in the Escorts.
-- @param Wrapper.Airbase#AIRBASE EscortAirbase The airbase where the escorts are spawned.
-- @param #string EscortName Name of the escort, which will also be the name of the escort menu.
-- @param #string EscortBriefing A text showing the briefing to the player. Note that if no EscortBriefing is provided, the default briefing will be shown.
-- @return #AI_ESCORT_DISPATCHER
-- @usage
--
-- -- Create a new escort when a player joins an SU-25T plane.
-- Create a carrier set, which contains the player slots that can be joined by the players, for which escorts will be defined.
-- local Red_SU25T_CarrierSet = SET_GROUP:New():FilterPrefixes( "Red A2G Player Su-25T" ):FilterStart()
--
-- -- Create a spawn object that will spawn in the escorts, once the player has joined the player slot.
-- local Red_SU25T_EscortSpawn = SPAWN:NewWithAlias( "Red A2G Su-25 Escort", "Red AI A2G SU-25 Escort" ):InitLimit( 10, 10 )
--
-- -- Create an airbase object, where the escorts will be spawned.
-- local Red_SU25T_Airbase = AIRBASE:FindByName( AIRBASE.Caucasus.Maykop_Khanskaya )
--
-- -- Park the airplanes at the airbase, visible before start.
-- Red_SU25T_EscortSpawn:ParkAtAirbase( Red_SU25T_Airbase, AIRBASE.TerminalType.OpenMedOrBig )
--
-- -- New create the escort dispatcher, using the carrier set, the escort spawn object at the escort airbase.
-- -- Provide a name of the escort, which will be also the name appearing on the radio menu for the group.
-- -- And a briefing to appear when the player joins the player slot.
-- Red_SU25T_EscortDispatcher = AI_ESCORT_DISPATCHER:New( Red_SU25T_CarrierSet, Red_SU25T_EscortSpawn, Red_SU25T_Airbase, "Escort Su-25", "You Su-25T is escorted by one Su-25. Use the radio menu to control the escorts." )
--
-- -- The dispatcher needs to be started using the :Start() method.
-- Red_SU25T_EscortDispatcher:Start()
function AI_ESCORT_DISPATCHER:New( CarrierSet, EscortSpawn, EscortAirbase, EscortName, EscortBriefing )
local self = BASE:Inherit( self, FSM:New() ) -- #AI_ESCORT_DISPATCHER
self.CarrierSet = CarrierSet
self.EscortSpawn = EscortSpawn
self.EscortAirbase = EscortAirbase
self.EscortName = EscortName
self.EscortBriefing = EscortBriefing
self:SetStartState( "Idle" )
self:AddTransition( "Monitoring", "Monitor", "Monitoring" )
self:AddTransition( "Idle", "Start", "Monitoring" )
self:AddTransition( "Monitoring", "Stop", "Idle" )
-- Put a Dead event handler on CarrierSet, to ensure that when a carrier is destroyed, that all internal parameters are reset.
function self.CarrierSet.OnAfterRemoved( CarrierSet, From, Event, To, CarrierName, Carrier )
self:F( { Carrier = Carrier:GetName() } )
end
return self
end
function AI_ESCORT_DISPATCHER:onafterStart( From, Event, To )
self:HandleEvent( EVENTS.Birth )
self:HandleEvent( EVENTS.PlayerLeaveUnit, self.OnEventExit )
self:HandleEvent( EVENTS.Crash, self.OnEventExit )
self:HandleEvent( EVENTS.Dead, self.OnEventExit )
end
-- @param #AI_ESCORT_DISPATCHER self
-- @param Core.Event#EVENTDATA EventData
function AI_ESCORT_DISPATCHER:OnEventExit( EventData )
local PlayerGroupName = EventData.IniGroupName
local PlayerGroup = EventData.IniGroup
local PlayerUnit = EventData.IniUnit
self:T({EscortAirbase= self.EscortAirbase } )
self:T({PlayerGroupName = PlayerGroupName } )
self:T({PlayerGroup = PlayerGroup})
self:T({FirstGroup = self.CarrierSet:GetFirst()})
self:T({FindGroup = self.CarrierSet:FindGroup( PlayerGroupName )})
if self.CarrierSet:FindGroup( PlayerGroupName ) then
if self.AI_Escorts[PlayerGroupName] then
self.AI_Escorts[PlayerGroupName]:Stop()
self.AI_Escorts[PlayerGroupName] = nil
end
end
end
-- @param #AI_ESCORT_DISPATCHER self
-- @param Core.Event#EVENTDATA EventData
function AI_ESCORT_DISPATCHER:OnEventBirth( EventData )
local PlayerGroupName = EventData.IniGroupName
local PlayerGroup = EventData.IniGroup
local PlayerUnit = EventData.IniUnit
self:T({EscortAirbase= self.EscortAirbase } )
self:T({PlayerGroupName = PlayerGroupName } )
self:T({PlayerGroup = PlayerGroup})
self:T({FirstGroup = self.CarrierSet:GetFirst()})
self:T({FindGroup = self.CarrierSet:FindGroup( PlayerGroupName )})
if self.CarrierSet:FindGroup( PlayerGroupName ) then
if not self.AI_Escorts[PlayerGroupName] then
local LeaderUnit = PlayerUnit
local EscortGroup = self.EscortSpawn:SpawnAtAirbase( self.EscortAirbase, SPAWN.Takeoff.Hot )
self:T({EscortGroup = EscortGroup})
self:ScheduleOnce( 1,
function( EscortGroup )
local EscortSet = SET_GROUP:New()
EscortSet:AddGroup( EscortGroup )
self.AI_Escorts[PlayerGroupName] = AI_ESCORT:New( LeaderUnit, EscortSet, self.EscortName, self.EscortBriefing )
self.AI_Escorts[PlayerGroupName]:FormationTrail( 0, 100, 0 )
if EscortGroup:IsHelicopter() then
self.AI_Escorts[PlayerGroupName]:MenusHelicopters()
else
self.AI_Escorts[PlayerGroupName]:MenusAirplanes()
end
self.AI_Escorts[PlayerGroupName]:__Start( 0.1 )
end, EscortGroup
)
end
end
end
--- Start Trigger for AI_ESCORT_DISPATCHER
-- @function [parent=#AI_ESCORT_DISPATCHER] Start
-- @param #AI_ESCORT_DISPATCHER self
--- Start Asynchronous Trigger for AI_ESCORT_DISPATCHER
-- @function [parent=#AI_ESCORT_DISPATCHER] __Start
-- @param #AI_ESCORT_DISPATCHER self
-- @param #number Delay
--- Stop Trigger for AI_ESCORT_DISPATCHER
-- @function [parent=#AI_ESCORT_DISPATCHER] Stop
-- @param #AI_ESCORT_DISPATCHER self
--- Stop Asynchronous Trigger for AI_ESCORT_DISPATCHER
-- @function [parent=#AI_ESCORT_DISPATCHER] __Stop
-- @param #AI_ESCORT_DISPATCHER self
-- @param #number Delay

View File

@@ -1,151 +0,0 @@
--- **AI** - Models the assignment of AI escorts to player flights upon request using the radio menu.
--
-- ## Features:
--
-- * Provides the facilities to trigger escorts when players join flight units.
-- * Provide a menu for which escorts can be requested.
--
-- ===
--
-- ### Author: **FlightControl**
--
-- ===
--
-- @module AI.AI_Escort_Dispatcher_Request
-- @image MOOSE.JPG
-- @type AI_ESCORT_DISPATCHER_REQUEST
-- @extends Core.Fsm#FSM
--- Models the assignment of AI escorts to player flights upon request using the radio menu.
--
-- # Developer Note
--
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
-- Therefore, this class is considered to be deprecated
--
-- ===
--
-- @field #AI_ESCORT_DISPATCHER_REQUEST
AI_ESCORT_DISPATCHER_REQUEST = {
ClassName = "AI_ESCORT_DISPATCHER_REQUEST",
}
-- @field #list
AI_ESCORT_DISPATCHER_REQUEST.AI_Escorts = {}
--- Creates a new AI_ESCORT_DISPATCHER_REQUEST object.
-- @param #AI_ESCORT_DISPATCHER_REQUEST self
-- @param Core.Set#SET_GROUP CarrierSet The set of @{Wrapper.Group#GROUP} objects of carriers for which escorts are requested.
-- @param Core.Spawn#SPAWN EscortSpawn The spawn object that will spawn in the Escorts.
-- @param Wrapper.Airbase#AIRBASE EscortAirbase The airbase where the escorts are spawned.
-- @param #string EscortName Name of the escort, which will also be the name of the escort menu.
-- @param #string EscortBriefing A text showing the briefing to the player. Note that if no EscortBriefing is provided, the default briefing will be shown.
-- @return #AI_ESCORT_DISPATCHER_REQUEST
function AI_ESCORT_DISPATCHER_REQUEST:New( CarrierSet, EscortSpawn, EscortAirbase, EscortName, EscortBriefing )
local self = BASE:Inherit( self, FSM:New() ) -- #AI_ESCORT_DISPATCHER_REQUEST
self.CarrierSet = CarrierSet
self.EscortSpawn = EscortSpawn
self.EscortAirbase = EscortAirbase
self.EscortName = EscortName
self.EscortBriefing = EscortBriefing
self:SetStartState( "Idle" )
self:AddTransition( "Monitoring", "Monitor", "Monitoring" )
self:AddTransition( "Idle", "Start", "Monitoring" )
self:AddTransition( "Monitoring", "Stop", "Idle" )
-- Put a Dead event handler on CarrierSet, to ensure that when a carrier is destroyed, that all internal parameters are reset.
function self.CarrierSet.OnAfterRemoved( CarrierSet, From, Event, To, CarrierName, Carrier )
self:F( { Carrier = Carrier:GetName() } )
end
return self
end
function AI_ESCORT_DISPATCHER_REQUEST:onafterStart( From, Event, To )
self:HandleEvent( EVENTS.Birth )
self:HandleEvent( EVENTS.PlayerLeaveUnit, self.OnEventExit )
self:HandleEvent( EVENTS.Crash, self.OnEventExit )
self:HandleEvent( EVENTS.Dead, self.OnEventExit )
end
-- @param #AI_ESCORT_DISPATCHER_REQUEST self
-- @param Core.Event#EVENTDATA EventData
function AI_ESCORT_DISPATCHER_REQUEST:OnEventExit( EventData )
local PlayerGroupName = EventData.IniGroupName
local PlayerGroup = EventData.IniGroup
local PlayerUnit = EventData.IniUnit
if self.CarrierSet:FindGroup( PlayerGroupName ) then
if self.AI_Escorts[PlayerGroupName] then
self.AI_Escorts[PlayerGroupName]:Stop()
self.AI_Escorts[PlayerGroupName] = nil
end
end
end
-- @param #AI_ESCORT_DISPATCHER_REQUEST self
-- @param Core.Event#EVENTDATA EventData
function AI_ESCORT_DISPATCHER_REQUEST:OnEventBirth( EventData )
local PlayerGroupName = EventData.IniGroupName
local PlayerGroup = EventData.IniGroup
local PlayerUnit = EventData.IniUnit
if self.CarrierSet:FindGroup( PlayerGroupName ) then
if not self.AI_Escorts[PlayerGroupName] then
local LeaderUnit = PlayerUnit
self:ScheduleOnce( 0.1,
function()
self.AI_Escorts[PlayerGroupName] = AI_ESCORT_REQUEST:New( LeaderUnit, self.EscortSpawn, self.EscortAirbase, self.EscortName, self.EscortBriefing )
self.AI_Escorts[PlayerGroupName]:FormationTrail( 0, 100, 0 )
if PlayerGroup:IsHelicopter() then
self.AI_Escorts[PlayerGroupName]:MenusHelicopters()
else
self.AI_Escorts[PlayerGroupName]:MenusAirplanes()
end
self.AI_Escorts[PlayerGroupName]:__Start( 0.1 )
end
)
end
end
end
--- Start Trigger for AI_ESCORT_DISPATCHER_REQUEST
-- @function [parent=#AI_ESCORT_DISPATCHER_REQUEST] Start
-- @param #AI_ESCORT_DISPATCHER_REQUEST self
--- Start Asynchronous Trigger for AI_ESCORT_DISPATCHER_REQUEST
-- @function [parent=#AI_ESCORT_DISPATCHER_REQUEST] __Start
-- @param #AI_ESCORT_DISPATCHER_REQUEST self
-- @param #number Delay
--- Stop Trigger for AI_ESCORT_DISPATCHER_REQUEST
-- @function [parent=#AI_ESCORT_DISPATCHER_REQUEST] Stop
-- @param #AI_ESCORT_DISPATCHER_REQUEST self
--- Stop Asynchronous Trigger for AI_ESCORT_DISPATCHER_REQUEST
-- @function [parent=#AI_ESCORT_DISPATCHER_REQUEST] __Stop
-- @param #AI_ESCORT_DISPATCHER_REQUEST self
-- @param #number Delay

View File

@@ -1,321 +0,0 @@
--- **AI** - Taking the lead of AI escorting your flight or of other AI, upon request using the menu.
--
-- ===
--
-- ## Features:
--
-- * Escort navigation commands.
-- * Escort hold at position commands.
-- * Escorts reporting detected targets.
-- * Escorts scanning targets in advance.
-- * Escorts attacking specific targets.
-- * Request assistance from other groups for attack.
-- * Manage rule of engagement of escorts.
-- * Manage the allowed evasion techniques of escorts.
-- * Make escort to execute a defined mission or path.
-- * Escort tactical situation reporting.
--
-- ===
--
-- ## Missions:
--
-- [ESC - Escorting](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AI/AI_Escort)
--
-- ===
--
-- Allows you to interact with escorting AI on your flight and take the lead.
--
-- Each escorting group can be commanded with a complete set of radio commands (radio menu in your flight, and then F10).
--
-- The radio commands will vary according the category of the group. The richest set of commands are with helicopters and airPlanes.
-- Ships and Ground troops will have a more limited set, but they can provide support through the bombing of targets designated by the other escorts.
--
-- Escorts detect targets using a built-in detection mechanism. The detected targets are reported at a specified time interval.
-- Once targets are reported, each escort has these targets as menu options to command the attack of these targets.
-- Targets are by default grouped per area of 5000 meters, but the kind of detection and the grouping range can be altered.
--
-- Different formations can be selected in the Flight menu: Trail, Stack, Left Line, Right Line, Left Wing, Right Wing, Central Wing and Boxed formations are available.
-- The Flight menu also allows for a mass attack, where all of the escorts are commanded to attack a target.
--
-- Escorts can emit flares to reports their location. They can be commanded to hold at a location, which can be their current or the leader location.
-- In this way, you can spread out the escorts over the battle field before a coordinated attack.
--
-- But basically, the escort class provides 4 modes of operation, and depending on the mode, you are either leading the flight, or following the flight.
--
-- ## Leading the flight
--
-- When leading the flight, you are expected to guide the escorts towards the target areas,
-- and carefully coordinate the attack based on the threat levels reported, and the available weapons
-- carried by the escorts. Ground ships or ground troops can execute A-assisted attacks, when they have long-range ground precision weapons for attack.
--
-- ## Following the flight
--
-- Escorts can be commanded to execute a specific mission path. In this mode, the escorts are in the lead.
-- You as a player, are following the escorts, and are commanding them to progress the mission while
-- ensuring that the escorts survive. You are joining the escorts in the battlefield. They will detect and report targets
-- and you will ensure that the attacks are well coordinated, assigning the correct escort type for the detected target
-- type. Once the attack is finished, the escort will resume the mission it was assigned.
-- In other words, you can use the escorts for reconnaissance, and for guiding the attack.
-- Imagine you as a mi-8 pilot, assigned to pickup cargo. Two ka-50s are guiding the way, and you are
-- following. You are in control. The ka-50s detect targets, report them, and you command how the attack
-- will commence and from where. You can control where the escorts are holding position and which targets
-- are attacked first. You are in control how the ka-50s will follow their mission path.
--
-- Escorts can act as part of a AI A2G dispatcher offensive. In this way, You was a player are in control.
-- The mission is defined by the A2G dispatcher, and you are responsible to join the flight and ensure that the
-- attack is well coordinated.
--
-- It is with great proud that I present you this class, and I hope you will enjoy the functionality and the dynamism
-- it brings in your DCS world simulations.
--
-- # RADIO MENUs that can be created:
--
-- Find a summary below of the current available commands:
--
-- ## Navigation ...:
--
-- Escort group navigation functions:
--
-- * **"Join-Up":** The escort group fill follow you in the assigned formation.
-- * **"Flare":** Provides menu commands to let the escort group shoot a flare in the air in a color.
-- * **"Smoke":** Provides menu commands to let the escort group smoke the air in a color. Note that smoking is only available for ground and naval troops.
--
-- ## Hold position ...:
--
-- Escort group navigation functions:
--
-- * **"At current location":** The escort group will hover above the ground at the position they were. The altitude can be specified as a parameter.
-- * **"At my location":** The escort group will hover or orbit at the position where you are. The escort will fly to your location and hold position. The altitude can be specified as a parameter.
--
-- ## Report targets ...:
--
-- Report targets will make the escort group to report any target that it identifies within detection range. Any detected target can be attacked using the "Attack Targets" menu function. (see below).
--
-- * **"Report now":** Will report the current detected targets.
-- * **"Report targets on":** Will make the escorts to report the detected targets and will fill the "Attack Targets" menu list.
-- * **"Report targets off":** Will stop detecting targets.
--
-- ## Attack targets ...:
--
-- This menu item will list all detected targets within a 15km range. Depending on the level of detection (known/unknown) and visuality, the targets type will also be listed.
-- This menu will be available in Flight menu or in each Escort menu.
--
-- ## Scan targets ...:
--
-- Menu items to pop-up the escort group for target scanning. After scanning, the escort group will resume with the mission or rejoin formation.
--
-- * **"Scan targets 30 seconds":** Scan 30 seconds for targets.
-- * **"Scan targets 60 seconds":** Scan 60 seconds for targets.
--
-- ## Request assistance from ...:
--
-- This menu item will list all detected targets within a 15km range, similar as with the menu item **Attack Targets**.
-- This menu item allows to request attack support from other ground based escorts supporting the current escort.
-- eg. the function allows a player to request support from the Ship escort to attack a target identified by the Plane escort with its Tomahawk missiles.
-- eg. the function allows a player to request support from other Planes escorting to bomb the unit with illumination missiles or bombs, so that the main plane escort can attack the area.
--
-- ## ROE ...:
--
-- Sets the Rules of Engagement (ROE) of the escort group when in flight.
--
-- * **"Hold Fire":** The escort group will hold fire.
-- * **"Return Fire":** The escort group will return fire.
-- * **"Open Fire":** The escort group will open fire on designated targets.
-- * **"Weapon Free":** The escort group will engage with any target.
--
-- ## Evasion ...:
--
-- Will define the evasion techniques that the escort group will perform during flight or combat.
--
-- * **"Fight until death":** The escort group will have no reaction to threats.
-- * **"Use flares, chaff and jammers":** The escort group will use passive defense using flares and jammers. No evasive manoeuvres are executed.
-- * **"Evade enemy fire":** The rescort group will evade enemy fire before firing.
-- * **"Go below radar and evade fire":** The escort group will perform evasive vertical manoeuvres.
--
-- ## Resume Mission ...:
--
-- Escort groups can have their own mission. This menu item will allow the escort group to resume their Mission from a given waypoint.
-- Note that this is really fantastic, as you now have the dynamic of taking control of the escort groups, and allowing them to resume their path or mission.
--
-- # Developer Note
--
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
-- Therefore, this class is considered to be deprecated
--
-- ===
--
-- ### Authors: **FlightControl**
--
-- ===
--
-- @module AI.AI_Escort_Request
-- @image Escorting.JPG
-- @type AI_ESCORT_REQUEST
-- @extends AI.AI_Escort#AI_ESCORT
--- AI_ESCORT_REQUEST class
--
-- # AI_ESCORT_REQUEST construction methods.
--
-- Create a new AI_ESCORT_REQUEST object with the @{#AI_ESCORT_REQUEST.New} method:
--
-- * @{#AI_ESCORT_REQUEST.New}: Creates a new AI_ESCORT_REQUEST object from a @{Wrapper.Group#GROUP} for a @{Wrapper.Client#CLIENT}, with an optional briefing text.
--
-- @usage
-- -- Declare a new EscortPlanes object as follows:
--
-- -- First find the GROUP object and the CLIENT object.
-- local EscortUnit = CLIENT:FindByName( "Unit Name" ) -- The Unit Name is the name of the unit flagged with the skill Client in the mission editor.
-- local EscortGroup = GROUP:FindByName( "Group Name" ) -- The Group Name is the name of the group that will escort the Escort Client.
--
-- -- Now use these 2 objects to construct the new EscortPlanes object.
-- EscortPlanes = AI_ESCORT_REQUEST:New( EscortUnit, EscortGroup, "Desert", "Welcome to the mission. You are escorted by a plane with code name 'Desert', which can be instructed through the F10 radio menu." )
--
-- @field #AI_ESCORT_REQUEST
AI_ESCORT_REQUEST = {
ClassName = "AI_ESCORT_REQUEST",
}
--- AI_ESCORT_REQUEST.Mode class
-- @type AI_ESCORT_REQUEST.MODE
-- @field #number FOLLOW
-- @field #number MISSION
--- MENUPARAM type
-- @type MENUPARAM
-- @field #AI_ESCORT_REQUEST ParamSelf
-- @field #Distance ParamDistance
-- @field #function ParamFunction
-- @field #string ParamMessage
--- AI_ESCORT_REQUEST class constructor for an AI group
-- @param #AI_ESCORT_REQUEST self
-- @param Wrapper.Client#CLIENT EscortUnit The client escorted by the EscortGroup.
-- @param Core.Spawn#SPAWN EscortSpawn The spawn object of AI, escorting the EscortUnit.
-- @param Wrapper.Airbase#AIRBASE EscortAirbase The airbase where escorts will be spawned once requested.
-- @param #string EscortName Name of the escort.
-- @param #string EscortBriefing A text showing the AI_ESCORT_REQUEST briefing to the player. Note that if no EscortBriefing is provided, the default briefing will be shown.
-- @return #AI_ESCORT_REQUEST
-- @usage
-- EscortSpawn = SPAWN:NewWithAlias( "Red A2G Escort Template", "Red A2G Escort AI" ):InitLimit( 10, 10 )
-- EscortSpawn:ParkAtAirbase( AIRBASE:FindByName( AIRBASE.Caucasus.Sochi_Adler ), AIRBASE.TerminalType.OpenBig )
--
-- local EscortUnit = UNIT:FindByName( "Red A2G Pilot" )
--
-- Escort = AI_ESCORT_REQUEST:New( EscortUnit, EscortSpawn, AIRBASE:FindByName(AIRBASE.Caucasus.Sochi_Adler), "A2G", "Briefing" )
-- Escort:FormationTrail( 50, 100, 100 )
-- Escort:Menus()
-- Escort:__Start( 5 )
function AI_ESCORT_REQUEST:New( EscortUnit, EscortSpawn, EscortAirbase, EscortName, EscortBriefing )
local EscortGroupSet = SET_GROUP:New():FilterDeads():FilterCrashes()
local self = BASE:Inherit( self, AI_ESCORT:New( EscortUnit, EscortGroupSet, EscortName, EscortBriefing ) ) -- #AI_ESCORT_REQUEST
self.EscortGroupSet = EscortGroupSet
self.EscortSpawn = EscortSpawn
self.EscortAirbase = EscortAirbase
self.LeaderGroup = self.PlayerUnit:GetGroup()
self.Detection = DETECTION_AREAS:New( self.EscortGroupSet, 5000 )
self.Detection:__Start( 30 )
self.SpawnMode = self.__Enum.Mode.Mission
return self
end
-- @param #AI_ESCORT_REQUEST self
function AI_ESCORT_REQUEST:SpawnEscort()
local EscortGroup = self.EscortSpawn:SpawnAtAirbase( self.EscortAirbase, SPAWN.Takeoff.Hot )
self:ScheduleOnce( 0.1,
function( EscortGroup )
EscortGroup:OptionROTVertical()
EscortGroup:OptionROEHoldFire()
self.EscortGroupSet:AddGroup( EscortGroup )
local LeaderEscort = self.EscortGroupSet:GetFirst() -- Wrapper.Group#GROUP
local Report = REPORT:New()
Report:Add( "Joining Up " .. self.EscortGroupSet:GetUnitTypeNames():Text( ", " ) .. " from " .. LeaderEscort:GetCoordinate():ToString( self.EscortUnit ) )
LeaderEscort:MessageTypeToGroup( Report:Text(), MESSAGE.Type.Information, self.PlayerUnit )
self:SetFlightModeFormation( EscortGroup )
self:FormationTrail()
self:_InitFlightMenus()
self:_InitEscortMenus( EscortGroup )
self:_InitEscortRoute( EscortGroup )
-- @param #AI_ESCORT self
-- @param Core.Event#EVENTDATA EventData
function EscortGroup:OnEventDeadOrCrash( EventData )
self:F( { "EventDead", EventData } )
self.EscortMenu:Remove()
end
EscortGroup:HandleEvent( EVENTS.Dead, EscortGroup.OnEventDeadOrCrash )
EscortGroup:HandleEvent( EVENTS.Crash, EscortGroup.OnEventDeadOrCrash )
end, EscortGroup
)
end
-- @param #AI_ESCORT_REQUEST self
-- @param Core.Set#SET_GROUP EscortGroupSet
function AI_ESCORT_REQUEST:onafterStart( EscortGroupSet )
self:F()
if not self.MenuRequestEscort then
self.MainMenu = MENU_GROUP:New( self.PlayerGroup, self.EscortName )
self.MenuRequestEscort = MENU_GROUP_COMMAND:New( self.LeaderGroup, "Request new escort ", self.MainMenu,
function()
self:SpawnEscort()
end
)
end
self:GetParent( self ).onafterStart( self, EscortGroupSet )
self:HandleEvent( EVENTS.Dead, self.OnEventDeadOrCrash )
self:HandleEvent( EVENTS.Crash, self.OnEventDeadOrCrash )
end
-- @param #AI_ESCORT_REQUEST self
-- @param Core.Set#SET_GROUP EscortGroupSet
function AI_ESCORT_REQUEST:onafterStop( EscortGroupSet )
self:F()
EscortGroupSet:ForEachGroup(
-- @param Core.Group#GROUP EscortGroup
function( EscortGroup )
EscortGroup:WayPointInitialize()
EscortGroup:OptionROTVertical()
EscortGroup:OptionROEOpenFire()
end
)
self.Detection:Stop()
self.MainMenu:Remove()
end
--- Set the spawn mode to be mission execution.
-- @param #AI_ESCORT_REQUEST self
function AI_ESCORT_REQUEST:SetEscortSpawnMission()
self.SpawnMode = self.__Enum.Mode.Mission
end

File diff suppressed because it is too large Load Diff

View File

@@ -1,939 +0,0 @@
--- **AI** - Perform Air Patrolling for airplanes.
--
-- **Features:**
--
-- * Patrol AI airplanes within a given zone.
-- * Trigger detected events when enemy airplanes are detected.
-- * Manage a fuel threshold to RTB on time.
--
-- ===
--
-- AI PATROL classes makes AI Controllables execute an Patrol.
--
-- There are the following types of PATROL classes defined:
--
-- * @{#AI_PATROL_ZONE}: Perform a PATROL in a zone.
--
-- ===
--
-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AI/AI_Patrol)
--
-- ===
--
-- ### [YouTube Playlist](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl35HvYZKA6G22WMt7iI3zky)
--
-- ===
--
-- ### Author: **FlightControl**
-- ### Contributions:
--
-- * **Dutch_Baron**: Working together with James has resulted in the creation of the AI_BALANCER class. James has shared his ideas on balancing AI with air units, and together we made a first design which you can use now :-)
-- * **Pikey**: Testing and API concept review.
--
-- ===
--
-- @module AI.AI_Patrol
-- @image AI_Air_Patrolling.JPG
--- AI_PATROL_ZONE class
-- @type AI_PATROL_ZONE
-- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Wrapper.Controllable} patrolling.
-- @field Core.Zone#ZONE_BASE PatrolZone The @{Core.Zone} where the patrol needs to be executed.
-- @field DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
-- @field DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
-- @field DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Controllable} in km/h.
-- @field DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Controllable} in km/h.
-- @field Core.Spawn#SPAWN CoordTest
-- @extends Core.Fsm#FSM_CONTROLLABLE
--- Implements the core functions to patrol a @{Core.Zone} by an AI @{Wrapper.Controllable} or @{Wrapper.Group}.
--
-- ![Process](..\Presentations\AI_PATROL\Dia3.JPG)
--
-- The AI_PATROL_ZONE is assigned a @{Wrapper.Group} and this must be done before the AI_PATROL_ZONE process can be started using the **Start** event.
--
-- ![Process](..\Presentations\AI_PATROL\Dia4.JPG)
--
-- The AI will fly towards the random 3D point within the patrol zone, using a random speed within the given altitude and speed limits.
-- Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits.
--
-- ![Process](..\Presentations\AI_PATROL\Dia5.JPG)
--
-- This cycle will continue.
--
-- ![Process](..\Presentations\AI_PATROL\Dia6.JPG)
--
-- During the patrol, the AI will detect enemy targets, which are reported through the **Detected** event.
--
-- ![Process](..\Presentations\AI_PATROL\Dia9.JPG)
--
---- Note that the enemy is not engaged! To model enemy engagement, either tailor the **Detected** event, or
-- use derived AI_ classes to model AI offensive or defensive behaviour.
--
-- ![Process](..\Presentations\AI_PATROL\Dia10.JPG)
--
-- Until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
-- When the fuel threshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
--
-- ![Process](..\Presentations\AI_PATROL\Dia11.JPG)
--
-- ## 1. AI_PATROL_ZONE constructor
--
-- * @{#AI_PATROL_ZONE.New}(): Creates a new AI_PATROL_ZONE object.
--
-- ## 2. AI_PATROL_ZONE is a FSM
--
-- ![Process](..\Presentations\AI_PATROL\Dia2.JPG)
--
-- ### 2.1. AI_PATROL_ZONE States
--
-- * **None** ( Group ): The process is not started yet.
-- * **Patrolling** ( Group ): The AI is patrolling the Patrol Zone.
-- * **Returning** ( Group ): The AI is returning to Base.
-- * **Stopped** ( Group ): The process is stopped.
-- * **Crashed** ( Group ): The AI has crashed or is dead.
--
-- ### 2.2. AI_PATROL_ZONE Events
--
-- * **Start** ( Group ): Start the process.
-- * **Stop** ( Group ): Stop the process.
-- * **Route** ( Group ): Route the AI to a new random 3D point within the Patrol Zone.
-- * **RTB** ( Group ): Route the AI to the home base.
-- * **Detect** ( Group ): The AI is detecting targets.
-- * **Detected** ( Group ): The AI has detected new targets.
-- * **Status** ( Group ): The AI is checking status (fuel and damage). When the thresholds have been reached, the AI will RTB.
--
-- ## 3. Set or Get the AI controllable
--
-- * @{#AI_PATROL_ZONE.SetControllable}(): Set the AIControllable.
-- * @{#AI_PATROL_ZONE.GetControllable}(): Get the AIControllable.
--
-- ## 4. Set the Speed and Altitude boundaries of the AI controllable
--
-- * @{#AI_PATROL_ZONE.SetSpeed}(): Set the patrol speed boundaries of the AI, for the next patrol.
-- * @{#AI_PATROL_ZONE.SetAltitude}(): Set altitude boundaries of the AI, for the next patrol.
--
-- ## 5. Manage the detection process of the AI controllable
--
-- The detection process of the AI controllable can be manipulated.
-- Detection requires an amount of CPU power, which has an impact on your mission performance.
-- Only put detection on when absolutely necessary, and the frequency of the detection can also be set.
--
-- * @{#AI_PATROL_ZONE.SetDetectionOn}(): Set the detection on. The AI will detect for targets.
-- * @{#AI_PATROL_ZONE.SetDetectionOff}(): Set the detection off, the AI will not detect for targets. The existing target list will NOT be erased.
--
-- The detection frequency can be set with @{#AI_PATROL_ZONE.SetRefreshTimeInterval}( seconds ), where the amount of seconds specify how much seconds will be waited before the next detection.
-- Use the method @{#AI_PATROL_ZONE.GetDetectedUnits}() to obtain a list of the @{Wrapper.Unit}s detected by the AI.
--
-- The detection can be filtered to potential targets in a specific zone.
-- Use the method @{#AI_PATROL_ZONE.SetDetectionZone}() to set the zone where targets need to be detected.
-- Note that when the zone is too far away, or the AI is not heading towards the zone, or the AI is too high, no targets may be detected
-- according the weather conditions.
--
-- ## 6. Manage the "out of fuel" in the AI_PATROL_ZONE
--
-- When the AI is out of fuel, it is required that a new AI is started, before the old AI can return to the home base.
-- Therefore, with a parameter and a calculation of the distance to the home base, the fuel threshold is calculated.
-- When the fuel threshold is reached, the AI will continue for a given time its patrol task in orbit,
-- while a new AI is targeted to the AI_PATROL_ZONE.
-- Once the time is finished, the old AI will return to the base.
-- Use the method @{#AI_PATROL_ZONE.ManageFuel}() to have this process in place.
--
-- ## 7. Manage "damage" behaviour of the AI in the AI_PATROL_ZONE
--
-- When the AI is damaged, it is required that a new AIControllable is started. However, damage cannon be foreseen early on.
-- Therefore, when the damage threshold is reached, the AI will return immediately to the home base (RTB).
-- Use the method @{#AI_PATROL_ZONE.ManageDamage}() to have this process in place.
--
-- # Developer Note
--
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
-- Therefore, this class is considered to be deprecated
--
-- ===
--
-- @field #AI_PATROL_ZONE
AI_PATROL_ZONE = {
ClassName = "AI_PATROL_ZONE",
}
--- Creates a new AI_PATROL_ZONE object
-- @param #AI_PATROL_ZONE self
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Core.Zone} where the patrol needs to be executed.
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Controllable} in km/h.
-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Controllable} in km/h.
-- @param DCS#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO
-- @return #AI_PATROL_ZONE self
-- @usage
-- -- Define a new AI_PATROL_ZONE Object. This PatrolArea will patrol an AIControllable within PatrolZone between 3000 and 6000 meters, with a variying speed between 600 and 900 km/h.
-- PatrolZone = ZONE:New( 'PatrolZone' )
-- PatrolSpawn = SPAWN:New( 'Patrol Group' )
-- PatrolArea = AI_PATROL_ZONE:New( PatrolZone, 3000, 6000, 600, 900 )
function AI_PATROL_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType )
-- Inherits from BASE
local self = BASE:Inherit( self, FSM_CONTROLLABLE:New() ) -- #AI_PATROL_ZONE
self.PatrolZone = PatrolZone
self.PatrolFloorAltitude = PatrolFloorAltitude
self.PatrolCeilingAltitude = PatrolCeilingAltitude
self.PatrolMinSpeed = PatrolMinSpeed
self.PatrolMaxSpeed = PatrolMaxSpeed
-- defafult PatrolAltType to "BARO" if not specified
self.PatrolAltType = PatrolAltType or "BARO"
self:SetRefreshTimeInterval( 30 )
self.CheckStatus = true
self:ManageFuel( .2, 60 )
self:ManageDamage( 1 )
self.DetectedUnits = {} -- This table contains the targets detected during patrol.
self:SetStartState( "None" )
self:AddTransition( "*", "Stop", "Stopped" )
--- OnLeave Transition Handler for State Stopped.
-- @function [parent=#AI_PATROL_ZONE] OnLeaveStopped
-- @param #AI_PATROL_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnEnter Transition Handler for State Stopped.
-- @function [parent=#AI_PATROL_ZONE] OnEnterStopped
-- @param #AI_PATROL_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- OnBefore Transition Handler for Event Stop.
-- @function [parent=#AI_PATROL_ZONE] OnBeforeStop
-- @param #AI_PATROL_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Stop.
-- @function [parent=#AI_PATROL_ZONE] OnAfterStop
-- @param #AI_PATROL_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Stop.
-- @function [parent=#AI_PATROL_ZONE] Stop
-- @param #AI_PATROL_ZONE self
--- Asynchronous Event Trigger for Event Stop.
-- @function [parent=#AI_PATROL_ZONE] __Stop
-- @param #AI_PATROL_ZONE self
-- @param #number Delay The delay in seconds.
self:AddTransition( "None", "Start", "Patrolling" )
--- OnBefore Transition Handler for Event Start.
-- @function [parent=#AI_PATROL_ZONE] OnBeforeStart
-- @param #AI_PATROL_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Start.
-- @function [parent=#AI_PATROL_ZONE] OnAfterStart
-- @param #AI_PATROL_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Start.
-- @function [parent=#AI_PATROL_ZONE] Start
-- @param #AI_PATROL_ZONE self
--- Asynchronous Event Trigger for Event Start.
-- @function [parent=#AI_PATROL_ZONE] __Start
-- @param #AI_PATROL_ZONE self
-- @param #number Delay The delay in seconds.
--- OnLeave Transition Handler for State Patrolling.
-- @function [parent=#AI_PATROL_ZONE] OnLeavePatrolling
-- @param #AI_PATROL_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnEnter Transition Handler for State Patrolling.
-- @function [parent=#AI_PATROL_ZONE] OnEnterPatrolling
-- @param #AI_PATROL_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
self:AddTransition( "Patrolling", "Route", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_PATROL_ZONE.
--- OnBefore Transition Handler for Event Route.
-- @function [parent=#AI_PATROL_ZONE] OnBeforeRoute
-- @param #AI_PATROL_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Route.
-- @function [parent=#AI_PATROL_ZONE] OnAfterRoute
-- @param #AI_PATROL_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Route.
-- @function [parent=#AI_PATROL_ZONE] Route
-- @param #AI_PATROL_ZONE self
--- Asynchronous Event Trigger for Event Route.
-- @function [parent=#AI_PATROL_ZONE] __Route
-- @param #AI_PATROL_ZONE self
-- @param #number Delay The delay in seconds.
self:AddTransition( "*", "Status", "*" ) -- FSM_CONTROLLABLE Transition for type #AI_PATROL_ZONE.
--- OnBefore Transition Handler for Event Status.
-- @function [parent=#AI_PATROL_ZONE] OnBeforeStatus
-- @param #AI_PATROL_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Status.
-- @function [parent=#AI_PATROL_ZONE] OnAfterStatus
-- @param #AI_PATROL_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Status.
-- @function [parent=#AI_PATROL_ZONE] Status
-- @param #AI_PATROL_ZONE self
--- Asynchronous Event Trigger for Event Status.
-- @function [parent=#AI_PATROL_ZONE] __Status
-- @param #AI_PATROL_ZONE self
-- @param #number Delay The delay in seconds.
self:AddTransition( "*", "Detect", "*" ) -- FSM_CONTROLLABLE Transition for type #AI_PATROL_ZONE.
--- OnBefore Transition Handler for Event Detect.
-- @function [parent=#AI_PATROL_ZONE] OnBeforeDetect
-- @param #AI_PATROL_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Detect.
-- @function [parent=#AI_PATROL_ZONE] OnAfterDetect
-- @param #AI_PATROL_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Detect.
-- @function [parent=#AI_PATROL_ZONE] Detect
-- @param #AI_PATROL_ZONE self
--- Asynchronous Event Trigger for Event Detect.
-- @function [parent=#AI_PATROL_ZONE] __Detect
-- @param #AI_PATROL_ZONE self
-- @param #number Delay The delay in seconds.
self:AddTransition( "*", "Detected", "*" ) -- FSM_CONTROLLABLE Transition for type #AI_PATROL_ZONE.
--- OnBefore Transition Handler for Event Detected.
-- @function [parent=#AI_PATROL_ZONE] OnBeforeDetected
-- @param #AI_PATROL_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Detected.
-- @function [parent=#AI_PATROL_ZONE] OnAfterDetected
-- @param #AI_PATROL_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Detected.
-- @function [parent=#AI_PATROL_ZONE] Detected
-- @param #AI_PATROL_ZONE self
--- Asynchronous Event Trigger for Event Detected.
-- @function [parent=#AI_PATROL_ZONE] __Detected
-- @param #AI_PATROL_ZONE self
-- @param #number Delay The delay in seconds.
self:AddTransition( "*", "RTB", "Returning" ) -- FSM_CONTROLLABLE Transition for type #AI_PATROL_ZONE.
--- OnBefore Transition Handler for Event RTB.
-- @function [parent=#AI_PATROL_ZONE] OnBeforeRTB
-- @param #AI_PATROL_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event RTB.
-- @function [parent=#AI_PATROL_ZONE] OnAfterRTB
-- @param #AI_PATROL_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event RTB.
-- @function [parent=#AI_PATROL_ZONE] RTB
-- @param #AI_PATROL_ZONE self
--- Asynchronous Event Trigger for Event RTB.
-- @function [parent=#AI_PATROL_ZONE] __RTB
-- @param #AI_PATROL_ZONE self
-- @param #number Delay The delay in seconds.
--- OnLeave Transition Handler for State Returning.
-- @function [parent=#AI_PATROL_ZONE] OnLeaveReturning
-- @param #AI_PATROL_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnEnter Transition Handler for State Returning.
-- @function [parent=#AI_PATROL_ZONE] OnEnterReturning
-- @param #AI_PATROL_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
self:AddTransition( "*", "Reset", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_PATROL_ZONE.
self:AddTransition( "*", "Eject", "*" )
self:AddTransition( "*", "Crash", "Crashed" )
self:AddTransition( "*", "PilotDead", "*" )
return self
end
--- Sets (modifies) the minimum and maximum speed of the patrol.
-- @param #AI_PATROL_ZONE self
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Controllable} in km/h.
-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Controllable} in km/h.
-- @return #AI_PATROL_ZONE self
function AI_PATROL_ZONE:SetSpeed( PatrolMinSpeed, PatrolMaxSpeed )
self:F2( { PatrolMinSpeed, PatrolMaxSpeed } )
self.PatrolMinSpeed = PatrolMinSpeed
self.PatrolMaxSpeed = PatrolMaxSpeed
end
--- Sets the floor and ceiling altitude of the patrol.
-- @param #AI_PATROL_ZONE self
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
-- @return #AI_PATROL_ZONE self
function AI_PATROL_ZONE:SetAltitude( PatrolFloorAltitude, PatrolCeilingAltitude )
self:F2( { PatrolFloorAltitude, PatrolCeilingAltitude } )
self.PatrolFloorAltitude = PatrolFloorAltitude
self.PatrolCeilingAltitude = PatrolCeilingAltitude
end
-- * @{#AI_PATROL_ZONE.SetDetectionOn}(): Set the detection on. The AI will detect for targets.
-- * @{#AI_PATROL_ZONE.SetDetectionOff}(): Set the detection off, the AI will not detect for targets. The existing target list will NOT be erased.
--- Set the detection on. The AI will detect for targets.
-- @param #AI_PATROL_ZONE self
-- @return #AI_PATROL_ZONE self
function AI_PATROL_ZONE:SetDetectionOn()
self:F2()
self.DetectOn = true
end
--- Set the detection off. The AI will NOT detect for targets.
-- However, the list of already detected targets will be kept and can be enquired!
-- @param #AI_PATROL_ZONE self
-- @return #AI_PATROL_ZONE self
function AI_PATROL_ZONE:SetDetectionOff()
self:F2()
self.DetectOn = false
end
--- Set the status checking off.
-- @param #AI_PATROL_ZONE self
-- @return #AI_PATROL_ZONE self
function AI_PATROL_ZONE:SetStatusOff()
self:F2()
self.CheckStatus = false
end
--- Activate the detection. The AI will detect for targets if the Detection is switched On.
-- @param #AI_PATROL_ZONE self
-- @return #AI_PATROL_ZONE self
function AI_PATROL_ZONE:SetDetectionActivated()
self:F2()
self:ClearDetectedUnits()
self.DetectActivated = true
self:__Detect( -self.DetectInterval )
end
--- Deactivate the detection. The AI will NOT detect for targets.
-- @param #AI_PATROL_ZONE self
-- @return #AI_PATROL_ZONE self
function AI_PATROL_ZONE:SetDetectionDeactivated()
self:F2()
self:ClearDetectedUnits()
self.DetectActivated = false
end
--- Set the interval in seconds between each detection executed by the AI.
-- The list of already detected targets will be kept and updated.
-- Newly detected targets will be added, but already detected targets that were
-- not detected in this cycle, will NOT be removed!
-- The default interval is 30 seconds.
-- @param #AI_PATROL_ZONE self
-- @param #number Seconds The interval in seconds.
-- @return #AI_PATROL_ZONE self
function AI_PATROL_ZONE:SetRefreshTimeInterval( Seconds )
self:F2()
if Seconds then
self.DetectInterval = Seconds
else
self.DetectInterval = 30
end
end
--- Set the detection zone where the AI is detecting targets.
-- @param #AI_PATROL_ZONE self
-- @param Core.Zone#ZONE DetectionZone The zone where to detect targets.
-- @return #AI_PATROL_ZONE self
function AI_PATROL_ZONE:SetDetectionZone( DetectionZone )
self:F2()
if DetectionZone then
self.DetectZone = DetectionZone
else
self.DetectZone = nil
end
end
--- Gets a list of @{Wrapper.Unit#UNIT}s that were detected by the AI.
-- No filtering is applied, so, ANY detected UNIT can be in this list.
-- It is up to the mission designer to use the @{Wrapper.Unit} class and methods to filter the targets.
-- @param #AI_PATROL_ZONE self
-- @return #table The list of @{Wrapper.Unit#UNIT}s
function AI_PATROL_ZONE:GetDetectedUnits()
self:F2()
return self.DetectedUnits
end
--- Clears the list of @{Wrapper.Unit#UNIT}s that were detected by the AI.
-- @param #AI_PATROL_ZONE self
function AI_PATROL_ZONE:ClearDetectedUnits()
self:F2()
self.DetectedUnits = {}
end
--- When the AI is out of fuel, it is required that a new AI is started, before the old AI can return to the home base.
-- Therefore, with a parameter and a calculation of the distance to the home base, the fuel threshold is calculated.
-- When the fuel threshold is reached, the AI will continue for a given time its patrol task in orbit, while a new AIControllable is targeted to the AI_PATROL_ZONE.
-- Once the time is finished, the old AI will return to the base.
-- @param #AI_PATROL_ZONE self
-- @param #number PatrolFuelThresholdPercentage The threshold in percentage (between 0 and 1) when the AIControllable is considered to get out of fuel.
-- @param #number PatrolOutOfFuelOrbitTime The amount of seconds the out of fuel AIControllable will orbit before returning to the base.
-- @return #AI_PATROL_ZONE self
function AI_PATROL_ZONE:ManageFuel( PatrolFuelThresholdPercentage, PatrolOutOfFuelOrbitTime )
self.PatrolFuelThresholdPercentage = PatrolFuelThresholdPercentage
self.PatrolOutOfFuelOrbitTime = PatrolOutOfFuelOrbitTime
return self
end
--- When the AI is damaged beyond a certain threshold, it is required that the AI returns to the home base.
-- However, damage cannot be foreseen early on.
-- Therefore, when the damage threshold is reached,
-- the AI will return immediately to the home base (RTB).
-- Note that for groups, the average damage of the complete group will be calculated.
-- So, in a group of 4 airplanes, 2 lost and 2 with damage 0.2, the damage threshold will be 0.25.
-- @param #AI_PATROL_ZONE self
-- @param #number PatrolDamageThreshold The threshold in percentage (between 0 and 1) when the AI is considered to be damaged.
-- @return #AI_PATROL_ZONE self
function AI_PATROL_ZONE:ManageDamage( PatrolDamageThreshold )
self.PatrolManageDamage = true
self.PatrolDamageThreshold = PatrolDamageThreshold
return self
end
--- Defines a new patrol route using the @{#AI_PATROL_ZONE} parameters and settings.
-- @param #AI_PATROL_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #AI_PATROL_ZONE self
function AI_PATROL_ZONE:onafterStart( Controllable, From, Event, To )
self:F2()
self:__Route( 1 ) -- Route to the patrol point. The asynchronous trigger is important, because a spawned group and units takes at least one second to come live.
self:__Status( 60 ) -- Check status status every 30 seconds.
self:SetDetectionActivated()
self:HandleEvent( EVENTS.PilotDead, self.OnPilotDead )
self:HandleEvent( EVENTS.Crash, self.OnCrash )
self:HandleEvent( EVENTS.Ejection, self.OnEjection )
Controllable:OptionROEHoldFire()
Controllable:OptionROTVertical()
self.Controllable:OnReSpawn(
function( PatrolGroup )
self:T( "ReSpawn" )
self:__Reset( 1 )
self:__Route( 5 )
end
)
self:SetDetectionOn()
end
-- @param #AI_PATROL_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable+
function AI_PATROL_ZONE:onbeforeDetect( Controllable, From, Event, To )
return self.DetectOn and self.DetectActivated
end
-- @param #AI_PATROL_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable
function AI_PATROL_ZONE:onafterDetect( Controllable, From, Event, To )
local Detected = false
local DetectedTargets = Controllable:GetDetectedTargets()
for TargetID, Target in pairs( DetectedTargets or {} ) do
local TargetObject = Target.object
if TargetObject and TargetObject:isExist() and TargetObject.id_ < 50000000 then
local TargetUnit = UNIT:Find( TargetObject )
-- Check that target is alive due to issue https://github.com/FlightControl-Master/MOOSE/issues/1234
if TargetUnit and TargetUnit:IsAlive() then
local TargetUnitName = TargetUnit:GetName()
if self.DetectionZone then
if TargetUnit:IsInZone( self.DetectionZone ) then
self:T( {"Detected ", TargetUnit } )
if self.DetectedUnits[TargetUnit] == nil then
self.DetectedUnits[TargetUnit] = true
end
Detected = true
end
else
if self.DetectedUnits[TargetUnit] == nil then
self.DetectedUnits[TargetUnit] = true
end
Detected = true
end
end
end
end
self:__Detect( -self.DetectInterval )
if Detected == true then
self:__Detected( 1.5 )
end
end
-- @param Wrapper.Controllable#CONTROLLABLE AIControllable
-- This static method is called from the route path within the last task at the last waypoint of the Controllable.
-- Note that this method is required, as triggers the next route when patrolling for the Controllable.
function AI_PATROL_ZONE:_NewPatrolRoute( AIControllable )
local PatrolZone = AIControllable:GetState( AIControllable, "PatrolZone" ) -- PatrolCore.Zone#AI_PATROL_ZONE
PatrolZone:__Route( 1 )
end
--- Defines a new patrol route using the @{#AI_PATROL_ZONE} parameters and settings.
-- @param #AI_PATROL_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_PATROL_ZONE:onafterRoute( Controllable, From, Event, To )
self:F2()
-- When RTB, don't allow anymore the routing.
if From == "RTB" then
return
end
local life = self.Controllable:GetLife() or 0
if self.Controllable:IsAlive() and life > 1 then
-- Determine if the AIControllable is within the PatrolZone.
-- If not, make a waypoint within the to that the AIControllable will fly at maximum speed to that point.
local PatrolRoute = {}
-- Calculate the current route point of the controllable as the start point of the route.
-- However, when the controllable is not in the air,
-- the controllable current waypoint is probably the airbase...
-- Thus, if we would take the current waypoint as the startpoint, upon take-off, the controllable flies
-- immediately back to the airbase, and this is not correct.
-- Therefore, when on a runway, get as the current route point a random point within the PatrolZone.
-- This will make the plane fly immediately to the patrol zone.
if self.Controllable:InAir() == false then
self:T( "Not in the air, finding route path within PatrolZone" )
local CurrentVec2 = self.Controllable:GetVec2()
if not CurrentVec2 then return end
--Done: Create GetAltitude function for GROUP, and delete GetUnit(1).
local CurrentAltitude = self.Controllable:GetAltitude()
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
local ToPatrolZoneSpeed = self.PatrolMaxSpeed
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
self.PatrolAltType,
POINT_VEC3.RoutePointType.TakeOffParking,
POINT_VEC3.RoutePointAction.FromParkingArea,
ToPatrolZoneSpeed,
true
)
PatrolRoute[#PatrolRoute+1] = CurrentRoutePoint
else
self:T( "In the air, finding route path within PatrolZone" )
local CurrentVec2 = self.Controllable:GetVec2()
if not CurrentVec2 then return end
--DONE: Create GetAltitude function for GROUP, and delete GetUnit(1).
local CurrentAltitude = self.Controllable:GetAltitude()
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
local ToPatrolZoneSpeed = self.PatrolMaxSpeed
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
self.PatrolAltType,
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
ToPatrolZoneSpeed,
true
)
PatrolRoute[#PatrolRoute+1] = CurrentRoutePoint
end
--- Define a random point in the @{Core.Zone}. The AI will fly to that point within the zone.
--- Find a random 2D point in PatrolZone.
local ToTargetVec2 = self.PatrolZone:GetRandomVec2()
self:T2( ToTargetVec2 )
--- Define Speed and Altitude.
local ToTargetAltitude = math.random( self.PatrolFloorAltitude, self.PatrolCeilingAltitude )
local ToTargetSpeed = math.random( self.PatrolMinSpeed, self.PatrolMaxSpeed )
self:T2( { self.PatrolMinSpeed, self.PatrolMaxSpeed, ToTargetSpeed } )
--- Obtain a 3D @{Point} from the 2D point + altitude.
local ToTargetPointVec3 = POINT_VEC3:New( ToTargetVec2.x, ToTargetAltitude, ToTargetVec2.y )
--- Create a route point of type air.
local ToTargetRoutePoint = ToTargetPointVec3:WaypointAir(
self.PatrolAltType,
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
ToTargetSpeed,
true
)
--self.CoordTest:SpawnFromVec3( ToTargetPointVec3:GetVec3() )
--ToTargetPointVec3:SmokeRed()
PatrolRoute[#PatrolRoute+1] = ToTargetRoutePoint
--- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable...
self.Controllable:WayPointInitialize( PatrolRoute )
--- Do a trick, link the NewPatrolRoute function of the PATROLGROUP object to the AIControllable in a temporary variable ...
self.Controllable:SetState( self.Controllable, "PatrolZone", self )
self.Controllable:WayPointFunction( #PatrolRoute, 1, "AI_PATROL_ZONE:_NewPatrolRoute" )
--- NOW ROUTE THE GROUP!
self.Controllable:WayPointExecute( 1, 2 )
end
end
-- @param #AI_PATROL_ZONE self
function AI_PATROL_ZONE:onbeforeStatus()
return self.CheckStatus
end
-- @param #AI_PATROL_ZONE self
function AI_PATROL_ZONE:onafterStatus()
self:F2()
if self.Controllable and self.Controllable:IsAlive() then
local RTB = false
local Fuel = self.Controllable:GetFuelMin()
if Fuel < self.PatrolFuelThresholdPercentage then
self:T( self.Controllable:GetName() .. " is out of fuel:" .. Fuel .. ", RTB!" )
local OldAIControllable = self.Controllable
local OrbitTask = OldAIControllable:TaskOrbitCircle( math.random( self.PatrolFloorAltitude, self.PatrolCeilingAltitude ), self.PatrolMinSpeed )
local TimedOrbitTask = OldAIControllable:TaskControlled( OrbitTask, OldAIControllable:TaskCondition(nil,nil,nil,nil,self.PatrolOutOfFuelOrbitTime,nil ) )
OldAIControllable:SetTask( TimedOrbitTask, 10 )
RTB = true
else
end
-- TODO: Check GROUP damage function.
local Damage = self.Controllable:GetLife()
if Damage <= self.PatrolDamageThreshold then
self:T( self.Controllable:GetName() .. " is damaged:" .. Damage .. ", RTB!" )
RTB = true
end
if RTB == true then
self:RTB()
else
self:__Status( 60 ) -- Execute the Patrol event after 30 seconds.
end
end
end
-- @param #AI_PATROL_ZONE self
function AI_PATROL_ZONE:onafterRTB()
self:F2()
if self.Controllable and self.Controllable:IsAlive() then
self:SetDetectionOff()
self.CheckStatus = false
local PatrolRoute = {}
--- Calculate the current route point.
local CurrentVec2 = self.Controllable:GetVec2()
if not CurrentVec2 then return end
--DONE: Create GetAltitude function for GROUP, and delete GetUnit(1).
--local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude()
local CurrentAltitude = self.Controllable:GetAltitude()
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
local ToPatrolZoneSpeed = self.PatrolMaxSpeed
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
self.PatrolAltType,
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
ToPatrolZoneSpeed,
true
)
PatrolRoute[#PatrolRoute+1] = CurrentRoutePoint
--- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable...
self.Controllable:WayPointInitialize( PatrolRoute )
--- NOW ROUTE THE GROUP!
self.Controllable:WayPointExecute( 1, 1 )
end
end
-- @param #AI_PATROL_ZONE self
function AI_PATROL_ZONE:onafterDead()
self:SetDetectionOff()
self:SetStatusOff()
end
-- @param #AI_PATROL_ZONE self
-- @param Core.Event#EVENTDATA EventData
function AI_PATROL_ZONE:OnCrash( EventData )
if self.Controllable:IsAlive() and EventData.IniDCSGroupName == self.Controllable:GetName() then
if #self.Controllable:GetUnits() == 1 then
self:__Crash( 1, EventData )
end
end
end
-- @param #AI_PATROL_ZONE self
-- @param Core.Event#EVENTDATA EventData
function AI_PATROL_ZONE:OnEjection( EventData )
if self.Controllable:IsAlive() and EventData.IniDCSGroupName == self.Controllable:GetName() then
self:__Eject( 1, EventData )
end
end
-- @param #AI_PATROL_ZONE self
-- @param Core.Event#EVENTDATA EventData
function AI_PATROL_ZONE:OnPilotDead( EventData )
if self.Controllable:IsAlive() and EventData.IniDCSGroupName == self.Controllable:GetName() then
self:__PilotDead( 1, EventData )
end
end

View File

@@ -1,310 +0,0 @@
--- **Actions** - ACT_ACCOUNT_ classes **account for** (detect, count & report) various DCS events occurring on UNITs.
--
-- ![Banner Image](..\Presentations\ACT_ACCOUNT\Dia1.JPG)
--
-- ===
--
-- @module Actions.Act_Account
-- @image MOOSE.JPG
do -- ACT_ACCOUNT
--- # @{#ACT_ACCOUNT} FSM class, extends @{Core.Fsm#FSM_PROCESS}
--
-- ## ACT_ACCOUNT state machine:
--
-- This class is a state machine: it manages a process that is triggered by events causing state transitions to occur.
-- All derived classes from this class will start with the class name, followed by a \_. See the relevant derived class descriptions below.
-- Each derived class follows exactly the same process, using the same events and following the same state transitions,
-- but will have **different implementation behaviour** upon each event or state transition.
--
-- ### ACT_ACCOUNT States
--
-- * **Assigned**: The player is assigned.
-- * **Waiting**: Waiting for an event.
-- * **Report**: Reporting.
-- * **Account**: Account for an event.
-- * **Accounted**: All events have been accounted for, end of the process.
-- * **Failed**: Failed the process.
--
-- ### ACT_ACCOUNT Events
--
-- * **Start**: Start the process.
-- * **Wait**: Wait for an event.
-- * **Report**: Report the status of the accounting.
-- * **Event**: An event happened, process the event.
-- * **More**: More targets.
-- * **NoMore (*)**: No more targets.
-- * **Fail (*)**: The action process has failed.
--
-- (*) End states of the process.
--
-- ### ACT_ACCOUNT state transition methods:
--
-- State transition functions can be set **by the mission designer** customizing or improving the behaviour of the state.
-- There are 2 moments when state transition methods will be called by the state machine:
--
-- * **Before** the state transition.
-- The state transition method needs to start with the name **OnBefore + the name of the state**.
-- If the state transition method returns false, then the processing of the state transition will not be done!
-- If you want to change the behaviour of the AIControllable at this event, return false,
-- but then you'll need to specify your own logic using the AIControllable!
--
-- * **After** the state transition.
-- The state transition method needs to start with the name **OnAfter + the name of the state**.
-- These state transition methods need to provide a return value, which is specified at the function description.
--
-- # Developer Note
--
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
-- Therefore, this class is considered to be deprecated
--
-- @type ACT_ACCOUNT
-- @field Core.Set#SET_UNIT TargetSetUnit
-- @extends Core.Fsm#FSM_PROCESS
ACT_ACCOUNT = {
ClassName = "ACT_ACCOUNT",
TargetSetUnit = nil,
}
--- Creates a new DESTROY process.
-- @param #ACT_ACCOUNT self
-- @return #ACT_ACCOUNT
function ACT_ACCOUNT:New()
-- Inherits from BASE
local self = BASE:Inherit( self, FSM_PROCESS:New() ) -- Core.Fsm#FSM_PROCESS
self:AddTransition( "Assigned", "Start", "Waiting" )
self:AddTransition( "*", "Wait", "Waiting" )
self:AddTransition( "*", "Report", "Report" )
self:AddTransition( "*", "Event", "Account" )
self:AddTransition( "Account", "Player", "AccountForPlayer" )
self:AddTransition( "Account", "Other", "AccountForOther" )
self:AddTransition( { "Account", "AccountForPlayer", "AccountForOther" }, "More", "Wait" )
self:AddTransition( { "Account", "AccountForPlayer", "AccountForOther" }, "NoMore", "Accounted" )
self:AddTransition( "*", "Fail", "Failed" )
self:AddEndState( "Failed" )
self:SetStartState( "Assigned" )
return self
end
--- Process Events
--- StateMachine callback function
-- @param #ACT_ACCOUNT self
-- @param Wrapper.Unit#UNIT ProcessUnit
-- @param #string Event
-- @param #string From
-- @param #string To
function ACT_ACCOUNT:onafterStart( ProcessUnit, From, Event, To )
self:HandleEvent( EVENTS.Dead, self.onfuncEventDead )
self:HandleEvent( EVENTS.Crash, self.onfuncEventCrash )
self:HandleEvent( EVENTS.Hit )
self:__Wait( 1 )
end
--- StateMachine callback function
-- @param #ACT_ACCOUNT self
-- @param Wrapper.Unit#UNIT ProcessUnit
-- @param #string Event
-- @param #string From
-- @param #string To
function ACT_ACCOUNT:onenterWaiting( ProcessUnit, From, Event, To )
if self.DisplayCount >= self.DisplayInterval then
self:Report()
self.DisplayCount = 1
else
self.DisplayCount = self.DisplayCount + 1
end
return true -- Process always the event.
end
--- StateMachine callback function
-- @param #ACT_ACCOUNT self
-- @param Wrapper.Unit#UNIT ProcessUnit
-- @param #string Event
-- @param #string From
-- @param #string To
function ACT_ACCOUNT:onafterEvent( ProcessUnit, From, Event, To, Event )
self:__NoMore( 1 )
end
end -- ACT_ACCOUNT
do -- ACT_ACCOUNT_DEADS
--- # @{#ACT_ACCOUNT_DEADS} FSM class, extends @{#ACT_ACCOUNT}
--
-- The ACT_ACCOUNT_DEADS class accounts (detects, counts and reports) successful kills of DCS units.
-- The process is given a @{Core.Set} of units that will be tracked upon successful destruction.
-- The process will end after each target has been successfully destroyed.
-- Each successful dead will trigger an Account state transition that can be scored, modified or administered.
--
--
-- ## ACT_ACCOUNT_DEADS constructor:
--
-- * @{#ACT_ACCOUNT_DEADS.New}(): Creates a new ACT_ACCOUNT_DEADS object.
--
-- @type ACT_ACCOUNT_DEADS
-- @field Core.Set#SET_UNIT TargetSetUnit
-- @extends #ACT_ACCOUNT
ACT_ACCOUNT_DEADS = {
ClassName = "ACT_ACCOUNT_DEADS",
}
--- Creates a new DESTROY process.
-- @param #ACT_ACCOUNT_DEADS self
-- @param Core.Set#SET_UNIT TargetSetUnit
-- @param #string TaskName
function ACT_ACCOUNT_DEADS:New()
-- Inherits from BASE
local self = BASE:Inherit( self, ACT_ACCOUNT:New() ) -- #ACT_ACCOUNT_DEADS
self.DisplayInterval = 30
self.DisplayCount = 30
self.DisplayMessage = true
self.DisplayTime = 10 -- 10 seconds is the default
self.DisplayCategory = "HQ" -- Targets is the default display category
return self
end
function ACT_ACCOUNT_DEADS:Init( FsmAccount )
self.Task = self:GetTask()
self.TaskName = self.Task:GetName()
end
--- Process Events
--- StateMachine callback function
-- @param #ACT_ACCOUNT_DEADS self
-- @param Wrapper.Unit#UNIT ProcessUnit
-- @param #string Event
-- @param #string From
-- @param #string To
function ACT_ACCOUNT_DEADS:onenterReport( ProcessUnit, Task, From, Event, To )
local MessageText = "Your group with assigned " .. self.TaskName .. " task has " .. Task.TargetSetUnit:GetUnitTypesText() .. " targets left to be destroyed."
self:GetCommandCenter():MessageTypeToGroup( MessageText, ProcessUnit:GetGroup(), MESSAGE.Type.Information )
end
--- StateMachine callback function
-- @param #ACT_ACCOUNT_DEADS self
-- @param Wrapper.Unit#UNIT ProcessUnit
-- @param Tasking.Task#TASK Task
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param Core.Event#EVENTDATA EventData
function ACT_ACCOUNT_DEADS:onafterEvent( ProcessUnit, Task, From, Event, To, EventData )
self:T( { ProcessUnit:GetName(), Task:GetName(), From, Event, To, EventData } )
if Task.TargetSetUnit:FindUnit( EventData.IniUnitName ) then
local PlayerName = ProcessUnit:GetPlayerName()
local PlayerHit = self.PlayerHits and self.PlayerHits[EventData.IniUnitName]
if PlayerHit == PlayerName then
self:Player( EventData )
else
self:Other( EventData )
end
end
end
--- StateMachine callback function
-- @param #ACT_ACCOUNT_DEADS self
-- @param Wrapper.Unit#UNIT ProcessUnit
-- @param Tasking.Task#TASK Task
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param Core.Event#EVENTDATA EventData
function ACT_ACCOUNT_DEADS:onenterAccountForPlayer( ProcessUnit, Task, From, Event, To, EventData )
self:T( { ProcessUnit:GetName(), Task:GetName(), From, Event, To, EventData } )
local TaskGroup = ProcessUnit:GetGroup()
Task.TargetSetUnit:Remove( EventData.IniUnitName )
local MessageText = "You have destroyed a target.\nYour group assigned with task " .. self.TaskName .. " has\n" .. Task.TargetSetUnit:Count() .. " targets ( " .. Task.TargetSetUnit:GetUnitTypesText() .. " ) left to be destroyed."
self:GetCommandCenter():MessageTypeToGroup( MessageText, ProcessUnit:GetGroup(), MESSAGE.Type.Information )
local PlayerName = ProcessUnit:GetPlayerName()
Task:AddProgress( PlayerName, "Destroyed " .. EventData.IniTypeName, timer.getTime(), 1 )
if Task.TargetSetUnit:Count() > 0 then
self:__More( 1 )
else
self:__NoMore( 1 )
end
end
--- StateMachine callback function
-- @param #ACT_ACCOUNT_DEADS self
-- @param Wrapper.Unit#UNIT ProcessUnit
-- @param Tasking.Task#TASK Task
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param Core.Event#EVENTDATA EventData
function ACT_ACCOUNT_DEADS:onenterAccountForOther( ProcessUnit, Task, From, Event, To, EventData )
self:T( { ProcessUnit:GetName(), Task:GetName(), From, Event, To, EventData } )
local TaskGroup = ProcessUnit:GetGroup()
Task.TargetSetUnit:Remove( EventData.IniUnitName )
local MessageText = "One of the task targets has been destroyed.\nYour group assigned with task " .. self.TaskName .. " has\n" .. Task.TargetSetUnit:Count() .. " targets ( " .. Task.TargetSetUnit:GetUnitTypesText() .. " ) left to be destroyed."
self:GetCommandCenter():MessageTypeToGroup( MessageText, ProcessUnit:GetGroup(), MESSAGE.Type.Information )
if Task.TargetSetUnit:Count() > 0 then
self:__More( 1 )
else
self:__NoMore( 1 )
end
end
--- DCS Events
-- @param #ACT_ACCOUNT_DEADS self
-- @param Core.Event#EVENTDATA EventData
function ACT_ACCOUNT_DEADS:OnEventHit( EventData )
self:T( { "EventDead", EventData } )
if EventData.IniPlayerName and EventData.TgtDCSUnitName then
self.PlayerHits = self.PlayerHits or {}
self.PlayerHits[EventData.TgtDCSUnitName] = EventData.IniPlayerName
end
end
-- @param #ACT_ACCOUNT_DEADS self
-- @param Core.Event#EVENTDATA EventData
function ACT_ACCOUNT_DEADS:onfuncEventDead( EventData )
self:T( { "EventDead", EventData } )
if EventData.IniDCSUnit then
self:Event( EventData )
end
end
--- DCS Events
-- @param #ACT_ACCOUNT_DEADS self
-- @param Core.Event#EVENTDATA EventData
function ACT_ACCOUNT_DEADS:onfuncEventCrash( EventData )
self:T( { "EventDead", EventData } )
if EventData.IniDCSUnit then
self:Event( EventData )
end
end
end -- ACT_ACCOUNT DEADS

View File

@@ -1,292 +0,0 @@
--- (SP) (MP) (FSM) Accept or reject process for player (task) assignments.
--
-- ===
--
-- # @{#ACT_ASSIGN} FSM template class, extends @{Core.Fsm#FSM_PROCESS}
--
-- ## ACT_ASSIGN state machine:
--
-- This class is a state machine: it manages a process that is triggered by events causing state transitions to occur.
-- All derived classes from this class will start with the class name, followed by a \_. See the relevant derived class descriptions below.
-- Each derived class follows exactly the same process, using the same events and following the same state transitions,
-- but will have **different implementation behaviour** upon each event or state transition.
--
-- ### ACT_ASSIGN **Events**:
--
-- These are the events defined in this class:
--
-- * **Start**: Start the tasking acceptance process.
-- * **Assign**: Assign the task.
-- * **Reject**: Reject the task..
--
-- ### ACT_ASSIGN **Event methods**:
--
-- Event methods are available (dynamically allocated by the state machine), that accomodate for state transitions occurring in the process.
-- There are two types of event methods, which you can use to influence the normal mechanisms in the state machine:
--
-- * **Immediate**: The event method has exactly the name of the event.
-- * **Delayed**: The event method starts with a __ + the name of the event. The first parameter of the event method is a number value, expressing the delay in seconds when the event will be executed.
--
-- ### ACT_ASSIGN **States**:
--
-- * **UnAssigned**: The player has not accepted the task.
-- * **Assigned (*)**: The player has accepted the task.
-- * **Rejected (*)**: The player has not accepted the task.
-- * **Waiting**: The process is awaiting player feedback.
-- * **Failed (*)**: The process has failed.
--
-- (*) End states of the process.
--
-- ### ACT_ASSIGN state transition methods:
--
-- State transition functions can be set **by the mission designer** customizing or improving the behaviour of the state.
-- There are 2 moments when state transition methods will be called by the state machine:
--
-- * **Before** the state transition.
-- The state transition method needs to start with the name **OnBefore + the name of the state**.
-- If the state transition method returns false, then the processing of the state transition will not be done!
-- If you want to change the behaviour of the AIControllable at this event, return false,
-- but then you'll need to specify your own logic using the AIControllable!
--
-- * **After** the state transition.
-- The state transition method needs to start with the name **OnAfter + the name of the state**.
-- These state transition methods need to provide a return value, which is specified at the function description.
--
-- # Developer Note
--
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
-- Therefore, this class is considered to be deprecated
--
-- ===
--
-- # 1) @{#ACT_ASSIGN_ACCEPT} class, extends @{Core.Fsm#ACT_ASSIGN}
--
-- The ACT_ASSIGN_ACCEPT class accepts by default a task for a player. No player intervention is allowed to reject the task.
--
-- ## 1.1) ACT_ASSIGN_ACCEPT constructor:
--
-- * @{#ACT_ASSIGN_ACCEPT.New}(): Creates a new ACT_ASSIGN_ACCEPT object.
--
-- ===
--
-- # 2) @{#ACT_ASSIGN_MENU_ACCEPT} class, extends @{Core.Fsm#ACT_ASSIGN}
--
-- The ACT_ASSIGN_MENU_ACCEPT class accepts a task when the player accepts the task through an added menu option.
-- This assignment type is useful to conditionally allow the player to choose whether or not he would accept the task.
-- The assignment type also allows to reject the task.
--
-- ## 2.1) ACT_ASSIGN_MENU_ACCEPT constructor:
-- -----------------------------------------
--
-- * @{#ACT_ASSIGN_MENU_ACCEPT.New}(): Creates a new ACT_ASSIGN_MENU_ACCEPT object.
--
-- ===
--
-- @module Actions.Act_Assign
-- @image MOOSE.JPG
do -- ACT_ASSIGN
--- ACT_ASSIGN class
-- @type ACT_ASSIGN
-- @field Tasking.Task#TASK Task
-- @field Wrapper.Unit#UNIT ProcessUnit
-- @field Core.Zone#ZONE_BASE TargetZone
-- @extends Core.Fsm#FSM_PROCESS
ACT_ASSIGN = {
ClassName = "ACT_ASSIGN",
}
--- Creates a new task assignment state machine. The process will accept the task by default, no player intervention accepted.
-- @param #ACT_ASSIGN self
-- @return #ACT_ASSIGN The task acceptance process.
function ACT_ASSIGN:New()
-- Inherits from BASE
local self = BASE:Inherit( self, FSM_PROCESS:New( "ACT_ASSIGN" ) ) -- Core.Fsm#FSM_PROCESS
self:AddTransition( "UnAssigned", "Start", "Waiting" )
self:AddTransition( "Waiting", "Assign", "Assigned" )
self:AddTransition( "Waiting", "Reject", "Rejected" )
self:AddTransition( "*", "Fail", "Failed" )
self:AddEndState( "Assigned" )
self:AddEndState( "Rejected" )
self:AddEndState( "Failed" )
self:SetStartState( "UnAssigned" )
return self
end
end -- ACT_ASSIGN
do -- ACT_ASSIGN_ACCEPT
--- ACT_ASSIGN_ACCEPT class
-- @type ACT_ASSIGN_ACCEPT
-- @field Tasking.Task#TASK Task
-- @field Wrapper.Unit#UNIT ProcessUnit
-- @field Core.Zone#ZONE_BASE TargetZone
-- @extends #ACT_ASSIGN
ACT_ASSIGN_ACCEPT = {
ClassName = "ACT_ASSIGN_ACCEPT",
}
--- Creates a new task assignment state machine. The process will accept the task by default, no player intervention accepted.
-- @param #ACT_ASSIGN_ACCEPT self
-- @param #string TaskBriefing
function ACT_ASSIGN_ACCEPT:New( TaskBriefing )
local self = BASE:Inherit( self, ACT_ASSIGN:New() ) -- #ACT_ASSIGN_ACCEPT
self.TaskBriefing = TaskBriefing
return self
end
function ACT_ASSIGN_ACCEPT:Init( FsmAssign )
self.TaskBriefing = FsmAssign.TaskBriefing
end
--- StateMachine callback function
-- @param #ACT_ASSIGN_ACCEPT self
-- @param Wrapper.Unit#UNIT ProcessUnit
-- @param #string Event
-- @param #string From
-- @param #string To
function ACT_ASSIGN_ACCEPT:onafterStart( ProcessUnit, Task, From, Event, To )
self:__Assign( 1 )
end
--- StateMachine callback function
-- @param #ACT_ASSIGN_ACCEPT self
-- @param Wrapper.Unit#UNIT ProcessUnit
-- @param #string Event
-- @param #string From
-- @param #string To
function ACT_ASSIGN_ACCEPT:onenterAssigned( ProcessUnit, Task, From, Event, To, TaskGroup )
self.Task:Assign( ProcessUnit, ProcessUnit:GetPlayerName() )
end
end -- ACT_ASSIGN_ACCEPT
do -- ACT_ASSIGN_MENU_ACCEPT
--- ACT_ASSIGN_MENU_ACCEPT class
-- @type ACT_ASSIGN_MENU_ACCEPT
-- @field Tasking.Task#TASK Task
-- @field Wrapper.Unit#UNIT ProcessUnit
-- @field Core.Zone#ZONE_BASE TargetZone
-- @extends #ACT_ASSIGN
ACT_ASSIGN_MENU_ACCEPT = {
ClassName = "ACT_ASSIGN_MENU_ACCEPT",
}
--- Init.
-- @param #ACT_ASSIGN_MENU_ACCEPT self
-- @param #string TaskBriefing
-- @return #ACT_ASSIGN_MENU_ACCEPT self
function ACT_ASSIGN_MENU_ACCEPT:New( TaskBriefing )
-- Inherits from BASE
local self = BASE:Inherit( self, ACT_ASSIGN:New() ) -- #ACT_ASSIGN_MENU_ACCEPT
self.TaskBriefing = TaskBriefing
return self
end
--- Creates a new task assignment state machine. The process will request from the menu if it accepts the task, if not, the unit is removed from the simulator.
-- @param #ACT_ASSIGN_MENU_ACCEPT self
-- @param #string TaskBriefing
-- @return #ACT_ASSIGN_MENU_ACCEPT self
function ACT_ASSIGN_MENU_ACCEPT:Init( TaskBriefing )
self.TaskBriefing = TaskBriefing
return self
end
--- StateMachine callback function
-- @param #ACT_ASSIGN_MENU_ACCEPT self
-- @param Wrapper.Unit#UNIT ProcessUnit
-- @param #string Event
-- @param #string From
-- @param #string To
function ACT_ASSIGN_MENU_ACCEPT:onafterStart( ProcessUnit, Task, From, Event, To )
self:GetCommandCenter():MessageToGroup( "Task " .. self.Task:GetName() .. " has been assigned to you and your group!\nRead the briefing and use the Radio Menu (F10) / Task ... CONFIRMATION menu to accept or reject the task.\nYou have 2 minutes to accept, or the task assignment will be cancelled!", ProcessUnit:GetGroup(), 120 )
local TaskGroup = ProcessUnit:GetGroup()
self.Menu = MENU_GROUP:New( TaskGroup, "Task " .. self.Task:GetName() .. " CONFIRMATION" )
self.MenuAcceptTask = MENU_GROUP_COMMAND:New( TaskGroup, "Accept task " .. self.Task:GetName(), self.Menu, self.MenuAssign, self, TaskGroup )
self.MenuRejectTask = MENU_GROUP_COMMAND:New( TaskGroup, "Reject task " .. self.Task:GetName(), self.Menu, self.MenuReject, self, TaskGroup )
self:__Reject( 120, TaskGroup )
end
--- Menu function.
-- @param #ACT_ASSIGN_MENU_ACCEPT self
function ACT_ASSIGN_MENU_ACCEPT:MenuAssign( TaskGroup )
self:__Assign( -1, TaskGroup )
end
--- Menu function.
-- @param #ACT_ASSIGN_MENU_ACCEPT self
function ACT_ASSIGN_MENU_ACCEPT:MenuReject( TaskGroup )
self:__Reject( -1, TaskGroup )
end
--- StateMachine callback function
-- @param #ACT_ASSIGN_MENU_ACCEPT self
-- @param Wrapper.Unit#UNIT ProcessUnit
-- @param #string Event
-- @param #string From
-- @param #string To
function ACT_ASSIGN_MENU_ACCEPT:onafterAssign( ProcessUnit, Task, From, Event, To, TaskGroup )
self.Menu:Remove()
end
--- StateMachine callback function
-- @param #ACT_ASSIGN_MENU_ACCEPT self
-- @param Wrapper.Unit#UNIT ProcessUnit
-- @param #string Event
-- @param #string From
-- @param #string To
function ACT_ASSIGN_MENU_ACCEPT:onafterReject( ProcessUnit, Task, From, Event, To, TaskGroup )
self:F( { TaskGroup = TaskGroup } )
self.Menu:Remove()
--TODO: need to resolve this problem ... it has to do with the events ...
--self.Task:UnAssignFromUnit( ProcessUnit )needs to become a callback funtion call upon the event
self.Task:RejectGroup( TaskGroup )
end
--- StateMachine callback function
-- @param #ACT_ASSIGN_ACCEPT self
-- @param Wrapper.Unit#UNIT ProcessUnit
-- @param #string Event
-- @param #string From
-- @param #string To
function ACT_ASSIGN_MENU_ACCEPT:onenterAssigned( ProcessUnit, Task, From, Event, To, TaskGroup )
--self.Task:AssignToGroup( TaskGroup )
self.Task:Assign( ProcessUnit, ProcessUnit:GetPlayerName() )
end
end -- ACT_ASSIGN_MENU_ACCEPT

View File

@@ -1,219 +0,0 @@
--- (SP) (MP) (FSM) Route AI or players through waypoints or to zones.
--
-- ## ACT_ASSIST state machine:
--
-- This class is a state machine: it manages a process that is triggered by events causing state transitions to occur.
-- All derived classes from this class will start with the class name, followed by a \_. See the relevant derived class descriptions below.
-- Each derived class follows exactly the same process, using the same events and following the same state transitions,
-- but will have **different implementation behaviour** upon each event or state transition.
--
-- ### ACT_ASSIST **Events**:
--
-- These are the events defined in this class:
--
-- * **Start**: The process is started.
-- * **Next**: The process is smoking the targets in the given zone.
--
-- ### ACT_ASSIST **Event methods**:
--
-- Event methods are available (dynamically allocated by the state machine), that accomodate for state transitions occurring in the process.
-- There are two types of event methods, which you can use to influence the normal mechanisms in the state machine:
--
-- * **Immediate**: The event method has exactly the name of the event.
-- * **Delayed**: The event method starts with a __ + the name of the event. The first parameter of the event method is a number value, expressing the delay in seconds when the event will be executed.
--
-- ### ACT_ASSIST **States**:
--
-- * **None**: The controllable did not receive route commands.
-- * **AwaitSmoke (*)**: The process is awaiting to smoke the targets in the zone.
-- * **Smoking (*)**: The process is smoking the targets in the zone.
-- * **Failed (*)**: The process has failed.
--
-- (*) End states of the process.
--
-- ### ACT_ASSIST state transition methods:
--
-- State transition functions can be set **by the mission designer** customizing or improving the behaviour of the state.
-- There are 2 moments when state transition methods will be called by the state machine:
--
-- * **Before** the state transition.
-- The state transition method needs to start with the name **OnBefore + the name of the state**.
-- If the state transition method returns false, then the processing of the state transition will not be done!
-- If you want to change the behaviour of the AIControllable at this event, return false,
-- but then you'll need to specify your own logic using the AIControllable!
--
-- * **After** the state transition.
-- The state transition method needs to start with the name **OnAfter + the name of the state**.
-- These state transition methods need to provide a return value, which is specified at the function description.
--
-- ===
--
-- # 1) @{#ACT_ASSIST_SMOKE_TARGETS_ZONE} class, extends @{#ACT_ASSIST}
--
-- The ACT_ASSIST_SMOKE_TARGETS_ZONE class implements the core functions to smoke targets in a @{Core.Zone}.
-- The targets are smoked within a certain range around each target, simulating a realistic smoking behaviour.
-- At random intervals, a new target is smoked.
--
-- # 1.1) ACT_ASSIST_SMOKE_TARGETS_ZONE constructor:
--
-- * @{#ACT_ASSIST_SMOKE_TARGETS_ZONE.New}(): Creates a new ACT_ASSIST_SMOKE_TARGETS_ZONE object.
--
-- # Developer Note
--
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
-- Therefore, this class is considered to be deprecated
--
-- ===
--
-- @module Actions.Act_Assist
-- @image MOOSE.JPG
do -- ACT_ASSIST
--- ACT_ASSIST class
-- @type ACT_ASSIST
-- @extends Core.Fsm#FSM_PROCESS
ACT_ASSIST = {
ClassName = "ACT_ASSIST",
}
--- Creates a new target smoking state machine. The process will request from the menu if it accepts the task, if not, the unit is removed from the simulator.
-- @param #ACT_ASSIST self
-- @return #ACT_ASSIST
function ACT_ASSIST:New()
-- Inherits from BASE
local self = BASE:Inherit( self, FSM_PROCESS:New( "ACT_ASSIST" ) ) -- Core.Fsm#FSM_PROCESS
self:AddTransition( "None", "Start", "AwaitSmoke" )
self:AddTransition( "AwaitSmoke", "Next", "Smoking" )
self:AddTransition( "Smoking", "Next", "AwaitSmoke" )
self:AddTransition( "*", "Stop", "Success" )
self:AddTransition( "*", "Fail", "Failed" )
self:AddEndState( "Failed" )
self:AddEndState( "Success" )
self:SetStartState( "None" )
return self
end
--- Task Events
--- StateMachine callback function
-- @param #ACT_ASSIST self
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
-- @param #string Event
-- @param #string From
-- @param #string To
function ACT_ASSIST:onafterStart( ProcessUnit, From, Event, To )
local ProcessGroup = ProcessUnit:GetGroup()
local MissionMenu = self:GetMission():GetMenu( ProcessGroup )
local function MenuSmoke( MenuParam )
local self = MenuParam.self
local SmokeColor = MenuParam.SmokeColor
self.SmokeColor = SmokeColor
self:__Next( 1 )
end
self.Menu = MENU_GROUP:New( ProcessGroup, "Target acquisition", MissionMenu )
self.MenuSmokeBlue = MENU_GROUP_COMMAND:New( ProcessGroup, "Drop blue smoke on targets", self.Menu, MenuSmoke, { self = self, SmokeColor = SMOKECOLOR.Blue } )
self.MenuSmokeGreen = MENU_GROUP_COMMAND:New( ProcessGroup, "Drop green smoke on targets", self.Menu, MenuSmoke, { self = self, SmokeColor = SMOKECOLOR.Green } )
self.MenuSmokeOrange = MENU_GROUP_COMMAND:New( ProcessGroup, "Drop Orange smoke on targets", self.Menu, MenuSmoke, { self = self, SmokeColor = SMOKECOLOR.Orange } )
self.MenuSmokeRed = MENU_GROUP_COMMAND:New( ProcessGroup, "Drop Red smoke on targets", self.Menu, MenuSmoke, { self = self, SmokeColor = SMOKECOLOR.Red } )
self.MenuSmokeWhite = MENU_GROUP_COMMAND:New( ProcessGroup, "Drop White smoke on targets", self.Menu, MenuSmoke, { self = self, SmokeColor = SMOKECOLOR.White } )
end
--- StateMachine callback function
-- @param #ACT_ASSIST self
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
-- @param #string Event
-- @param #string From
-- @param #string To
function ACT_ASSIST:onafterStop( ProcessUnit, From, Event, To )
self.Menu:Remove() -- When stopped, remove the menus
end
end
do -- ACT_ASSIST_SMOKE_TARGETS_ZONE
--- ACT_ASSIST_SMOKE_TARGETS_ZONE class
-- @type ACT_ASSIST_SMOKE_TARGETS_ZONE
-- @field Core.Set#SET_UNIT TargetSetUnit
-- @field Core.Zone#ZONE_BASE TargetZone
-- @extends #ACT_ASSIST
ACT_ASSIST_SMOKE_TARGETS_ZONE = {
ClassName = "ACT_ASSIST_SMOKE_TARGETS_ZONE",
}
-- function ACT_ASSIST_SMOKE_TARGETS_ZONE:_Destructor()
-- self:E("_Destructor")
--
-- self.Menu:Remove()
-- self:EventRemoveAll()
-- end
--- Creates a new target smoking state machine. The process will request from the menu if it accepts the task, if not, the unit is removed from the simulator.
-- @param #ACT_ASSIST_SMOKE_TARGETS_ZONE self
-- @param Core.Set#SET_UNIT TargetSetUnit
-- @param Core.Zone#ZONE_BASE TargetZone
function ACT_ASSIST_SMOKE_TARGETS_ZONE:New( TargetSetUnit, TargetZone )
local self = BASE:Inherit( self, ACT_ASSIST:New() ) -- #ACT_ASSIST
self.TargetSetUnit = TargetSetUnit
self.TargetZone = TargetZone
return self
end
function ACT_ASSIST_SMOKE_TARGETS_ZONE:Init( FsmSmoke )
self.TargetSetUnit = FsmSmoke.TargetSetUnit
self.TargetZone = FsmSmoke.TargetZone
end
--- Creates a new target smoking state machine. The process will request from the menu if it accepts the task, if not, the unit is removed from the simulator.
-- @param #ACT_ASSIST_SMOKE_TARGETS_ZONE self
-- @param Core.Set#SET_UNIT TargetSetUnit
-- @param Core.Zone#ZONE_BASE TargetZone
-- @return #ACT_ASSIST_SMOKE_TARGETS_ZONE self
function ACT_ASSIST_SMOKE_TARGETS_ZONE:Init( TargetSetUnit, TargetZone )
self.TargetSetUnit = TargetSetUnit
self.TargetZone = TargetZone
return self
end
--- StateMachine callback function
-- @param #ACT_ASSIST_SMOKE_TARGETS_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
-- @param #string Event
-- @param #string From
-- @param #string To
function ACT_ASSIST_SMOKE_TARGETS_ZONE:onenterSmoking( ProcessUnit, From, Event, To )
self.TargetSetUnit:ForEachUnit(
-- @param Wrapper.Unit#UNIT SmokeUnit
function( SmokeUnit )
if math.random( 1, ( 100 * self.TargetSetUnit:Count() ) / 4 ) <= 100 then
SCHEDULER:New( self,
function()
if SmokeUnit:IsAlive() then
SmokeUnit:Smoke( self.SmokeColor, 150 )
end
end, {}, math.random( 10, 60 )
)
end
end
)
end
end

View File

@@ -1,483 +0,0 @@
--- (SP) (MP) (FSM) Route AI or players through waypoints or to zones.
--
-- ===
--
-- # @{#ACT_ROUTE} FSM class, extends @{Core.Fsm#FSM_PROCESS}
--
-- ## ACT_ROUTE state machine:
--
-- This class is a state machine: it manages a process that is triggered by events causing state transitions to occur.
-- All derived classes from this class will start with the class name, followed by a \_. See the relevant derived class descriptions below.
-- Each derived class follows exactly the same process, using the same events and following the same state transitions,
-- but will have **different implementation behaviour** upon each event or state transition.
--
-- ### ACT_ROUTE **Events**:
--
-- These are the events defined in this class:
--
-- * **Start**: The process is started. The process will go into the Report state.
-- * **Report**: The process is reporting to the player the route to be followed.
-- * **Route**: The process is routing the controllable.
-- * **Pause**: The process is pausing the route of the controllable.
-- * **Arrive**: The controllable has arrived at a route point.
-- * **More**: There are more route points that need to be followed. The process will go back into the Report state.
-- * **NoMore**: There are no more route points that need to be followed. The process will go into the Success state.
--
-- ### ACT_ROUTE **Event methods**:
--
-- Event methods are available (dynamically allocated by the state machine), that accomodate for state transitions occurring in the process.
-- There are two types of event methods, which you can use to influence the normal mechanisms in the state machine:
--
-- * **Immediate**: The event method has exactly the name of the event.
-- * **Delayed**: The event method starts with a __ + the name of the event. The first parameter of the event method is a number value, expressing the delay in seconds when the event will be executed.
--
-- ### ACT_ROUTE **States**:
--
-- * **None**: The controllable did not receive route commands.
-- * **Arrived (*)**: The controllable has arrived at a route point.
-- * **Aborted (*)**: The controllable has aborted the route path.
-- * **Routing**: The controllable is understay to the route point.
-- * **Pausing**: The process is pausing the routing. AI air will go into hover, AI ground will stop moving. Players can fly around.
-- * **Success (*)**: All route points were reached.
-- * **Failed (*)**: The process has failed.
--
-- (*) End states of the process.
--
-- ### ACT_ROUTE state transition methods:
--
-- State transition functions can be set **by the mission designer** customizing or improving the behaviour of the state.
-- There are 2 moments when state transition methods will be called by the state machine:
--
-- * **Before** the state transition.
-- The state transition method needs to start with the name **OnBefore + the name of the state**.
-- If the state transition method returns false, then the processing of the state transition will not be done!
-- If you want to change the behaviour of the AIControllable at this event, return false,
-- but then you'll need to specify your own logic using the AIControllable!
--
-- * **After** the state transition.
-- The state transition method needs to start with the name **OnAfter + the name of the state**.
-- These state transition methods need to provide a return value, which is specified at the function description.
--
-- ===
--
-- # 1) @{#ACT_ROUTE_ZONE} class, extends @{#ACT_ROUTE}
--
-- The ACT_ROUTE_ZONE class implements the core functions to route an AIR @{Wrapper.Controllable} player @{Wrapper.Unit} to a @{Core.Zone}.
-- The player receives on perioding times messages with the coordinates of the route to follow.
-- Upon arrival at the zone, a confirmation of arrival is sent, and the process will be ended.
--
-- # 1.1) ACT_ROUTE_ZONE constructor:
--
-- * @{#ACT_ROUTE_ZONE.New}(): Creates a new ACT_ROUTE_ZONE object.
--
-- # Developer Note
--
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
-- Therefore, this class is considered to be deprecated
--
-- ===
--
-- @module Actions.Act_Route
-- @image MOOSE.JPG
do -- ACT_ROUTE
--- ACT_ROUTE class
-- @type ACT_ROUTE
-- @field Tasking.Task#TASK TASK
-- @field Wrapper.Unit#UNIT ProcessUnit
-- @field Core.Zone#ZONE_BASE Zone
-- @field Core.Point#COORDINATE Coordinate
-- @extends Core.Fsm#FSM_PROCESS
ACT_ROUTE = {
ClassName = "ACT_ROUTE",
}
--- Creates a new routing state machine. The process will route a CLIENT to a ZONE until the CLIENT is within that ZONE.
-- @param #ACT_ROUTE self
-- @return #ACT_ROUTE self
function ACT_ROUTE:New()
-- Inherits from BASE
local self = BASE:Inherit( self, FSM_PROCESS:New( "ACT_ROUTE" ) ) -- Core.Fsm#FSM_PROCESS
self:AddTransition( "*", "Reset", "None" )
self:AddTransition( "None", "Start", "Routing" )
self:AddTransition( "*", "Report", "*" )
self:AddTransition( "Routing", "Route", "Routing" )
self:AddTransition( "Routing", "Pause", "Pausing" )
self:AddTransition( "Routing", "Arrive", "Arrived" )
self:AddTransition( "*", "Cancel", "Cancelled" )
self:AddTransition( "Arrived", "Success", "Success" )
self:AddTransition( "*", "Fail", "Failed" )
self:AddTransition( "", "", "" )
self:AddTransition( "", "", "" )
self:AddEndState( "Arrived" )
self:AddEndState( "Failed" )
self:AddEndState( "Cancelled" )
self:SetStartState( "None" )
self:SetRouteMode( "C" )
return self
end
--- Set a Cancel Menu item.
-- @param #ACT_ROUTE self
-- @return #ACT_ROUTE
function ACT_ROUTE:SetMenuCancel( MenuGroup, MenuText, ParentMenu, MenuTime, MenuTag )
self.CancelMenuGroupCommand = MENU_GROUP_COMMAND:New(
MenuGroup,
MenuText,
ParentMenu,
self.MenuCancel,
self
):SetTime( MenuTime ):SetTag( MenuTag )
ParentMenu:SetTime( MenuTime )
ParentMenu:Remove( MenuTime, MenuTag )
return self
end
--- Set the route mode.
-- There are 2 route modes supported:
--
-- * SetRouteMode( "B" ): Route mode is Bearing and Range.
-- * SetRouteMode( "C" ): Route mode is LL or MGRS according coordinate system setup.
--
-- @param #ACT_ROUTE self
-- @return #ACT_ROUTE
function ACT_ROUTE:SetRouteMode( RouteMode )
self.RouteMode = RouteMode
return self
end
--- Get the routing text to be displayed.
-- The route mode determines the text displayed.
-- @param #ACT_ROUTE self
-- @param Wrapper.Unit#UNIT Controllable
-- @return #string
function ACT_ROUTE:GetRouteText( Controllable )
local RouteText = ""
local Coordinate = nil -- Core.Point#COORDINATE
if self.Coordinate then
Coordinate = self.Coordinate
end
if self.Zone then
Coordinate = self.Zone:GetPointVec3( self.Altitude )
Coordinate:SetHeading( self.Heading )
end
local Task = self:GetTask() -- This is to dermine that the coordinates are for a specific task mode (A2A or A2G).
local CC = self:GetTask():GetMission():GetCommandCenter()
if CC then
if CC:IsModeWWII() then
-- Find closest reference point to the target.
local ShortestDistance = 0
local ShortestReferencePoint = nil
local ShortestReferenceName = ""
self:F( { CC.ReferencePoints } )
for ZoneName, Zone in pairs( CC.ReferencePoints ) do
self:F( { ZoneName = ZoneName } )
local Zone = Zone -- Core.Zone#ZONE
local ZoneCoord = Zone:GetCoordinate()
local ZoneDistance = ZoneCoord:Get2DDistance( Coordinate )
self:F( { ShortestDistance, ShortestReferenceName } )
if ShortestDistance == 0 or ZoneDistance < ShortestDistance then
ShortestDistance = ZoneDistance
ShortestReferencePoint = ZoneCoord
ShortestReferenceName = CC.ReferenceNames[ZoneName]
end
end
if ShortestReferencePoint then
RouteText = Coordinate:ToStringFromRP( ShortestReferencePoint, ShortestReferenceName, Controllable )
end
else
RouteText = Coordinate:ToString( Controllable, nil, Task )
end
end
return RouteText
end
function ACT_ROUTE:MenuCancel()
self:F("Cancelled")
self.CancelMenuGroupCommand:Remove()
self:__Cancel( 1 )
end
--- Task Events
--- StateMachine callback function
-- @param #ACT_ROUTE self
-- @param Wrapper.Unit#UNIT ProcessUnit
-- @param #string Event
-- @param #string From
-- @param #string To
function ACT_ROUTE:onafterStart( ProcessUnit, From, Event, To )
self:__Route( 1 )
end
--- Check if the controllable has arrived.
-- @param #ACT_ROUTE self
-- @param Wrapper.Unit#UNIT ProcessUnit
-- @return #boolean
function ACT_ROUTE:onfuncHasArrived( ProcessUnit )
return false
end
--- StateMachine callback function
-- @param #ACT_ROUTE self
-- @param Wrapper.Unit#UNIT ProcessUnit
-- @param #string Event
-- @param #string From
-- @param #string To
function ACT_ROUTE:onbeforeRoute( ProcessUnit, From, Event, To )
if ProcessUnit:IsAlive() then
local HasArrived = self:onfuncHasArrived( ProcessUnit ) -- Polymorphic
if self.DisplayCount >= self.DisplayInterval then
self:T( { HasArrived = HasArrived } )
if not HasArrived then
self:Report()
end
self.DisplayCount = 1
else
self.DisplayCount = self.DisplayCount + 1
end
if HasArrived then
self:__Arrive( 1 )
else
self:__Route( 1 )
end
return HasArrived -- if false, then the event will not be executed...
end
return false
end
end -- ACT_ROUTE
do -- ACT_ROUTE_POINT
--- ACT_ROUTE_POINT class
-- @type ACT_ROUTE_POINT
-- @field Tasking.Task#TASK TASK
-- @extends #ACT_ROUTE
ACT_ROUTE_POINT = {
ClassName = "ACT_ROUTE_POINT",
}
--- Creates a new routing state machine.
-- The task will route a controllable to a Coordinate until the controllable is within the Range.
-- @param #ACT_ROUTE_POINT self
-- @param Core.Point#COORDINATE The Coordinate to Target.
-- @param #number Range The Distance to Target.
-- @param Core.Zone#ZONE_BASE Zone
function ACT_ROUTE_POINT:New( Coordinate, Range )
local self = BASE:Inherit( self, ACT_ROUTE:New() ) -- #ACT_ROUTE_POINT
self.Coordinate = Coordinate
self.Range = Range or 0
self.DisplayInterval = 30
self.DisplayCount = 30
self.DisplayMessage = true
self.DisplayTime = 10 -- 10 seconds is the default
return self
end
--- Creates a new routing state machine.
-- The task will route a controllable to a Coordinate until the controllable is within the Range.
-- @param #ACT_ROUTE_POINT self
function ACT_ROUTE_POINT:Init( FsmRoute )
self.Coordinate = FsmRoute.Coordinate
self.Range = FsmRoute.Range or 0
self.DisplayInterval = 30
self.DisplayCount = 30
self.DisplayMessage = true
self.DisplayTime = 10 -- 10 seconds is the default
self:SetStartState("None")
end
--- Set Coordinate
-- @param #ACT_ROUTE_POINT self
-- @param Core.Point#COORDINATE Coordinate The Coordinate to route to.
function ACT_ROUTE_POINT:SetCoordinate( Coordinate )
self:F2( { Coordinate } )
self.Coordinate = Coordinate
end
--- Get Coordinate
-- @param #ACT_ROUTE_POINT self
-- @return Core.Point#COORDINATE Coordinate The Coordinate to route to.
function ACT_ROUTE_POINT:GetCoordinate()
self:F2( { self.Coordinate } )
return self.Coordinate
end
--- Set Range around Coordinate
-- @param #ACT_ROUTE_POINT self
-- @param #number Range The Range to consider the arrival. Default is 10000 meters.
function ACT_ROUTE_POINT:SetRange( Range )
self:F2( { Range } )
self.Range = Range or 10000
end
--- Get Range around Coordinate
-- @param #ACT_ROUTE_POINT self
-- @return #number The Range to consider the arrival. Default is 10000 meters.
function ACT_ROUTE_POINT:GetRange()
self:F2( { self.Range } )
return self.Range
end
--- Method override to check if the controllable has arrived.
-- @param #ACT_ROUTE_POINT self
-- @param Wrapper.Unit#UNIT ProcessUnit
-- @return #boolean
function ACT_ROUTE_POINT:onfuncHasArrived( ProcessUnit )
if ProcessUnit:IsAlive() then
local Distance = self.Coordinate:Get2DDistance( ProcessUnit:GetCoordinate() )
if Distance <= self.Range then
local RouteText = "Task \"" .. self:GetTask():GetName() .. "\", you have arrived."
self:GetCommandCenter():MessageTypeToGroup( RouteText, ProcessUnit:GetGroup(), MESSAGE.Type.Information )
return true
end
end
return false
end
--- Task Events
--- StateMachine callback function
-- @param #ACT_ROUTE_POINT self
-- @param Wrapper.Unit#UNIT ProcessUnit
-- @param #string Event
-- @param #string From
-- @param #string To
function ACT_ROUTE_POINT:onafterReport( ProcessUnit, From, Event, To )
local RouteText = "Task \"" .. self:GetTask():GetName() .. "\", " .. self:GetRouteText( ProcessUnit )
self:GetCommandCenter():MessageTypeToGroup( RouteText, ProcessUnit:GetGroup(), MESSAGE.Type.Update )
end
end -- ACT_ROUTE_POINT
do -- ACT_ROUTE_ZONE
--- ACT_ROUTE_ZONE class
-- @type ACT_ROUTE_ZONE
-- @field Tasking.Task#TASK TASK
-- @field Wrapper.Unit#UNIT ProcessUnit
-- @field Core.Zone#ZONE_BASE Zone
-- @extends #ACT_ROUTE
ACT_ROUTE_ZONE = {
ClassName = "ACT_ROUTE_ZONE",
}
--- Creates a new routing state machine. The task will route a controllable to a ZONE until the controllable is within that ZONE.
-- @param #ACT_ROUTE_ZONE self
-- @param Core.Zone#ZONE_BASE Zone
function ACT_ROUTE_ZONE:New( Zone )
local self = BASE:Inherit( self, ACT_ROUTE:New() ) -- #ACT_ROUTE_ZONE
self.Zone = Zone
self.DisplayInterval = 30
self.DisplayCount = 30
self.DisplayMessage = true
self.DisplayTime = 10 -- 10 seconds is the default
return self
end
function ACT_ROUTE_ZONE:Init( FsmRoute )
self.Zone = FsmRoute.Zone
self.DisplayInterval = 30
self.DisplayCount = 30
self.DisplayMessage = true
self.DisplayTime = 10 -- 10 seconds is the default
end
--- Set Zone
-- @param #ACT_ROUTE_ZONE self
-- @param Core.Zone#ZONE_BASE Zone The Zone object where to route to.
-- @param #number Altitude
-- @param #number Heading
function ACT_ROUTE_ZONE:SetZone( Zone, Altitude, Heading ) -- R2.2 Added altitude and heading
self.Zone = Zone
self.Altitude = Altitude
self.Heading = Heading
end
--- Get Zone
-- @param #ACT_ROUTE_ZONE self
-- @return Core.Zone#ZONE_BASE Zone The Zone object where to route to.
function ACT_ROUTE_ZONE:GetZone()
return self.Zone
end
--- Method override to check if the controllable has arrived.
-- @param #ACT_ROUTE self
-- @param Wrapper.Unit#UNIT ProcessUnit
-- @return #boolean
function ACT_ROUTE_ZONE:onfuncHasArrived( ProcessUnit )
if ProcessUnit:IsInZone( self.Zone ) then
local RouteText = "Task \"" .. self:GetTask():GetName() .. "\", you have arrived within the zone."
self:GetCommandCenter():MessageTypeToGroup( RouteText, ProcessUnit:GetGroup(), MESSAGE.Type.Information )
end
return ProcessUnit:IsInZone( self.Zone )
end
--- Task Events
--- StateMachine callback function
-- @param #ACT_ROUTE_ZONE self
-- @param Wrapper.Unit#UNIT ProcessUnit
-- @param #string Event
-- @param #string From
-- @param #string To
function ACT_ROUTE_ZONE:onafterReport( ProcessUnit, From, Event, To )
self:F( { ProcessUnit = ProcessUnit } )
local RouteText = "Task \"" .. self:GetTask():GetName() .. "\", " .. self:GetRouteText( ProcessUnit )
self:GetCommandCenter():MessageTypeToGroup( RouteText, ProcessUnit:GetGroup(), MESSAGE.Type.Update )
end
end -- ACT_ROUTE_ZONE

File diff suppressed because it is too large Load Diff

View File

@@ -1,337 +0,0 @@
--- **Cargo** - Management of single cargo crates, which are based on a STATIC object.
--
-- ===
--
-- ### [Demo Missions]()
--
-- ### [YouTube Playlist]()
--
-- ===
--
-- ### Author: **FlightControl**
-- ### Contributions:
--
-- ===
--
-- @module Cargo.CargoCrate
-- @image Cargo_Crates.JPG
do -- CARGO_CRATE
--- Models the behaviour of cargo crates, which can be slingloaded and boarded on helicopters.
-- @type CARGO_CRATE
-- @extends Cargo.Cargo#CARGO_REPRESENTABLE
--- Defines a cargo that is represented by a UNIT object within the simulator, and can be transported by a carrier.
-- Use the event functions as described above to Load, UnLoad, Board, UnBoard the CARGO\_CRATE objects to and from carriers.
--
-- The above cargo classes are used by the following AI_CARGO_ classes to allow AI groups to transport cargo:
--
-- * AI Armoured Personnel Carriers to transport cargo and engage in battles, using the @{AI.AI_Cargo_APC} module.
-- * AI Helicopters to transport cargo, using the @{AI.AI_Cargo_Helicopter} module.
-- * AI Planes to transport cargo, using the @{AI.AI_Cargo_Airplane} module.
-- * AI Ships is planned.
--
-- The above cargo classes are also used by the TASK_CARGO_ classes to allow human players to transport cargo as part of a tasking:
--
-- * @{Tasking.Task_Cargo_Transport#TASK_CARGO_TRANSPORT} to transport cargo by human players.
-- * @{Tasking.Task_Cargo_Transport#TASK_CARGO_CSAR} to transport downed pilots by human players.
--
-- # Developer Note
--
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
-- Therefore, this class is considered to be deprecated
--
-- ===
--
-- @field #CARGO_CRATE
CARGO_CRATE = {
ClassName = "CARGO_CRATE"
}
--- CARGO_CRATE Constructor.
-- @param #CARGO_CRATE self
-- @param Wrapper.Static#STATIC CargoStatic
-- @param #string Type
-- @param #string Name
-- @param #number LoadRadius (optional)
-- @param #number NearRadius (optional)
-- @return #CARGO_CRATE
function CARGO_CRATE:New( CargoStatic, Type, Name, LoadRadius, NearRadius )
local self = BASE:Inherit( self, CARGO_REPRESENTABLE:New( CargoStatic, Type, Name, nil, LoadRadius, NearRadius ) ) -- #CARGO_CRATE
self:T( { Type, Name, NearRadius } )
self.CargoObject = CargoStatic -- Wrapper.Static#STATIC
-- Cargo objects are added to the _DATABASE and SET_CARGO objects.
_EVENTDISPATCHER:CreateEventNewCargo( self )
self:HandleEvent( EVENTS.Dead, self.OnEventCargoDead )
self:HandleEvent( EVENTS.Crash, self.OnEventCargoDead )
--self:HandleEvent( EVENTS.RemoveUnit, self.OnEventCargoDead )
self:HandleEvent( EVENTS.PlayerLeaveUnit, self.OnEventCargoDead )
self:SetEventPriority( 4 )
self.NearRadius = NearRadius or 25
return self
end
-- @param #CARGO_CRATE self
-- @param Core.Event#EVENTDATA EventData
function CARGO_CRATE:OnEventCargoDead( EventData )
local Destroyed = false
if self:IsDestroyed() or self:IsUnLoaded() or self:IsBoarding() then
if self.CargoObject:GetName() == EventData.IniUnitName then
if not self.NoDestroy then
Destroyed = true
end
end
else
if self:IsLoaded() then
local CarrierName = self.CargoCarrier:GetName()
if CarrierName == EventData.IniDCSUnitName then
MESSAGE:New( "Cargo is lost from carrier " .. CarrierName, 15 ):ToAll()
Destroyed = true
self.CargoCarrier:ClearCargo()
end
end
end
if Destroyed then
self:I( { "Cargo crate destroyed: " .. self.CargoObject:GetName() } )
self:Destroyed()
end
end
--- Enter UnLoaded State.
-- @param #CARGO_CRATE self
-- @param #string Event
-- @param #string From
-- @param #string To
-- @param Core.Point#POINT_VEC2
function CARGO_CRATE:onenterUnLoaded( From, Event, To, ToPointVec2 )
--self:T( { ToPointVec2, From, Event, To } )
local Angle = 180
local Speed = 10
local Distance = 10
if From == "Loaded" then
local StartCoordinate = self.CargoCarrier:GetCoordinate()
local CargoCarrierHeading = self.CargoCarrier:GetHeading() -- Get Heading of object in degrees.
local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle )
local CargoDeployCoord = StartCoordinate:Translate( Distance, CargoDeployHeading )
ToPointVec2 = ToPointVec2 or COORDINATE:NewFromVec2( { x= CargoDeployCoord.x, y = CargoDeployCoord.z } )
-- Respawn the group...
if self.CargoObject then
self.CargoObject:ReSpawnAt( ToPointVec2, 0 )
self.CargoCarrier = nil
end
end
if self.OnUnLoadedCallBack then
self.OnUnLoadedCallBack( self, unpack( self.OnUnLoadedParameters ) )
self.OnUnLoadedCallBack = nil
end
end
--- Loaded State.
-- @param #CARGO_CRATE self
-- @param #string Event
-- @param #string From
-- @param #string To
-- @param Wrapper.Unit#UNIT CargoCarrier
function CARGO_CRATE:onenterLoaded( From, Event, To, CargoCarrier )
--self:T( { From, Event, To, CargoCarrier } )
self.CargoCarrier = CargoCarrier
-- Only destroy the CargoObject is if there is a CargoObject (packages don't have CargoObjects).
if self.CargoObject then
self:T("Destroying")
self.NoDestroy = true
self.CargoObject:Destroy( false ) -- Do not generate a remove unit event, because we want to keep the template for later respawn in the database.
--local Coordinate = self.CargoObject:GetCoordinate():GetRandomCoordinateInRadius( 50, 20 )
--self.CargoObject:ReSpawnAt( Coordinate, 0 )
end
end
--- Check if the cargo can be Boarded.
-- @param #CARGO_CRATE self
function CARGO_CRATE:CanBoard()
return false
end
--- Check if the cargo can be Unboarded.
-- @param #CARGO_CRATE self
function CARGO_CRATE:CanUnboard()
return false
end
--- Check if the cargo can be sling loaded.
-- @param #CARGO_CRATE self
function CARGO_CRATE:CanSlingload()
return false
end
--- Check if Cargo Crate is in the radius for the Cargo to be reported.
-- @param #CARGO_CRATE self
-- @param Core.Point#COORDINATE Coordinate
-- @return #boolean true if the Cargo Crate is within the report radius.
function CARGO_CRATE:IsInReportRadius( Coordinate )
--self:T( { Coordinate, LoadRadius = self.LoadRadius } )
local Distance = 0
if self:IsUnLoaded() then
Distance = Coordinate:Get2DDistance( self.CargoObject:GetCoordinate() )
--self:T( Distance )
if Distance <= self.LoadRadius then
return true
end
end
return false
end
--- Check if Cargo Crate is in the radius for the Cargo to be Boarded or Loaded.
-- @param #CARGO_CRATE self
-- @param Core.Point#Coordinate Coordinate
-- @return #boolean true if the Cargo Crate is within the loading radius.
function CARGO_CRATE:IsInLoadRadius( Coordinate )
--self:T( { Coordinate, LoadRadius = self.NearRadius } )
local Distance = 0
if self:IsUnLoaded() then
Distance = Coordinate:Get2DDistance( self.CargoObject:GetCoordinate() )
--self:T( Distance )
if Distance <= self.NearRadius then
return true
end
end
return false
end
--- Get the current Coordinate of the CargoGroup.
-- @param #CARGO_CRATE self
-- @return Core.Point#COORDINATE The current Coordinate of the first Cargo of the CargoGroup.
-- @return #nil There is no valid Cargo in the CargoGroup.
function CARGO_CRATE:GetCoordinate()
--self:T()
return self.CargoObject:GetCoordinate()
end
--- Check if the CargoGroup is alive.
-- @param #CARGO_CRATE self
-- @return #boolean true if the CargoGroup is alive.
-- @return #boolean false if the CargoGroup is dead.
function CARGO_CRATE:IsAlive()
local Alive = true
-- When the Cargo is Loaded, the Cargo is in the CargoCarrier, so we check if the CargoCarrier is alive.
-- When the Cargo is not Loaded, the Cargo is the CargoObject, so we check if the CargoObject is alive.
if self:IsLoaded() then
Alive = Alive == true and self.CargoCarrier:IsAlive()
else
Alive = Alive == true and self.CargoObject:IsAlive()
end
return Alive
end
--- Route Cargo to Coordinate and randomize locations.
-- @param #CARGO_CRATE self
-- @param Core.Point#COORDINATE Coordinate
function CARGO_CRATE:RouteTo( Coordinate )
self:T( {Coordinate = Coordinate } )
end
--- Check if Cargo is near to the Carrier.
-- The Cargo is near to the Carrier within NearRadius.
-- @param #CARGO_CRATE self
-- @param Wrapper.Group#GROUP CargoCarrier
-- @param #number NearRadius
-- @return #boolean The Cargo is near to the Carrier.
-- @return #nil The Cargo is not near to the Carrier.
function CARGO_CRATE:IsNear( CargoCarrier, NearRadius )
self:T( {NearRadius = NearRadius } )
return self:IsNear( CargoCarrier:GetCoordinate(), NearRadius )
end
--- Respawn the CargoGroup.
-- @param #CARGO_CRATE self
function CARGO_CRATE:Respawn()
self:T( { "Respawning crate " .. self:GetName() } )
-- Respawn the group...
if self.CargoObject then
self.CargoObject:ReSpawn() -- A cargo destroy crates a DEAD event.
self:__Reset( -0.1 )
end
end
--- Respawn the CargoGroup.
-- @param #CARGO_CRATE self
function CARGO_CRATE:onafterReset()
self:T( { "Reset crate " .. self:GetName() } )
-- Respawn the group...
if self.CargoObject then
self:SetDeployed( false )
self:SetStartState( "UnLoaded" )
self.CargoCarrier = nil
-- Cargo objects are added to the _DATABASE and SET_CARGO objects.
_EVENTDISPATCHER:CreateEventNewCargo( self )
end
end
--- Get the transportation method of the Cargo.
-- @param #CARGO_CRATE self
-- @return #string The transportation method of the Cargo.
function CARGO_CRATE:GetTransportationMethod()
if self:IsLoaded() then
return "for unloading"
else
if self:IsUnLoaded() then
return "for loading"
else
if self:IsDeployed() then
return "delivered"
end
end
end
return ""
end
end

View File

@@ -1,773 +0,0 @@
--- **Cargo** - Management of grouped cargo logistics, which are based on a GROUP object.
--
-- ===
--
-- ### [Demo Missions]()
--
-- ### [YouTube Playlist]()
--
-- ===
--
-- ### Author: **FlightControl**
-- ### Contributions:
--
-- ===
--
-- @module Cargo.CargoGroup
-- @image Cargo_Groups.JPG
do -- CARGO_GROUP
--- @type CARGO_GROUP
-- @field Core.Set#SET_CARGO CargoSet The collection of derived CARGO objects.
-- @field #string GroupName The name of the CargoGroup.
-- @extends Cargo.Cargo#CARGO_REPORTABLE
--- Defines a cargo that is represented by a @{Wrapper.Group} object within the simulator.
-- The cargo can be Loaded, UnLoaded, Boarded, UnBoarded to and from Carriers.
--
-- The above cargo classes are used by the following AI_CARGO_ classes to allow AI groups to transport cargo:
--
-- * AI Armoured Personnel Carriers to transport cargo and engage in battles, using the @{AI.AI_Cargo_APC} module.
-- * AI Helicopters to transport cargo, using the @{AI.AI_Cargo_Helicopter} module.
-- * AI Planes to transport cargo, using the @{AI.AI_Cargo_Airplane} module.
-- * AI Ships is planned.
--
-- The above cargo classes are also used by the TASK_CARGO_ classes to allow human players to transport cargo as part of a tasking:
--
-- * @{Tasking.Task_Cargo_Transport#TASK_CARGO_TRANSPORT} to transport cargo by human players.
-- * @{Tasking.Task_Cargo_Transport#TASK_CARGO_CSAR} to transport downed pilots by human players.
--
-- # Developer Note
--
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
-- Therefore, this class is considered to be deprecated
--
-- @field #CARGO_GROUP CARGO_GROUP
--
CARGO_GROUP = {
ClassName = "CARGO_GROUP",
}
--- CARGO_GROUP constructor.
-- This make a new CARGO_GROUP from a @{Wrapper.Group} object.
-- It will "ungroup" the group object within the sim, and will create a @{Core.Set} of individual Unit objects.
-- @param #CARGO_GROUP self
-- @param Wrapper.Group#GROUP CargoGroup Group to be transported as cargo.
-- @param #string Type Cargo type, e.g. "Infantry". This is the type used in SET_CARGO:New():FilterTypes("Infantry") to define the valid cargo groups of the set.
-- @param #string Name A user defined name of the cargo group. This name CAN be the same as the group object but can also have a different name. This name MUST be unique!
-- @param #number LoadRadius (optional) Distance in meters until which a cargo is loaded into the carrier. Cargo outside this radius has to be routed by other means to within the radius to be loaded.
-- @param #number NearRadius (optional) Once the units are within this radius of the carrier, they are actually loaded, i.e. disappear from the scene.
-- @return #CARGO_GROUP Cargo group object.
function CARGO_GROUP:New( CargoGroup, Type, Name, LoadRadius, NearRadius )
-- Inherit CAROG_REPORTABLE
local self = BASE:Inherit( self, CARGO_REPORTABLE:New( Type, Name, 0, LoadRadius, NearRadius ) ) -- #CARGO_GROUP
self:T( { Type, Name, LoadRadius } )
self.CargoSet = SET_CARGO:New()
self.CargoGroup = CargoGroup
self.Grouped = true
self.CargoUnitTemplate = {}
self.NearRadius = NearRadius
self:SetDeployed( false )
local WeightGroup = 0
local VolumeGroup = 0
self.CargoGroup:Destroy() -- destroy and generate a unit removal event, so that the database gets cleaned, and the linked sets get properly cleaned.
local GroupName = CargoGroup:GetName()
self.CargoName = Name
self.CargoTemplate = UTILS.DeepCopy( _DATABASE:GetGroupTemplate( GroupName ) )
-- Deactivate late activation.
self.CargoTemplate.lateActivation=false
self.GroupTemplate = UTILS.DeepCopy( self.CargoTemplate )
self.GroupTemplate.name = self.CargoName .. "#CARGO"
self.GroupTemplate.groupId = nil
self.GroupTemplate.units = {}
for UnitID, UnitTemplate in pairs( self.CargoTemplate.units ) do
UnitTemplate.name = UnitTemplate.name .. "#CARGO"
local CargoUnitName = UnitTemplate.name
self.CargoUnitTemplate[CargoUnitName] = UnitTemplate
self.GroupTemplate.units[#self.GroupTemplate.units+1] = self.CargoUnitTemplate[CargoUnitName]
self.GroupTemplate.units[#self.GroupTemplate.units].unitId = nil
-- And we register the spawned unit as part of the CargoSet.
local Unit = UNIT:Register( CargoUnitName )
end
-- Then we register the new group in the database
self.CargoGroup = GROUP:NewTemplate( self.GroupTemplate, self.GroupTemplate.CoalitionID, self.GroupTemplate.CategoryID, self.GroupTemplate.CountryID )
-- Now we spawn the new group based on the template created.
self.CargoObject = _DATABASE:Spawn( self.GroupTemplate )
for CargoUnitID, CargoUnit in pairs( self.CargoObject:GetUnits() ) do
local CargoUnitName = CargoUnit:GetName()
local Cargo = CARGO_UNIT:New( CargoUnit, Type, CargoUnitName, LoadRadius, NearRadius )
self.CargoSet:Add( CargoUnitName, Cargo )
WeightGroup = WeightGroup + Cargo:GetWeight()
end
self:SetWeight( WeightGroup )
self:T( { "Weight Cargo", WeightGroup } )
-- Cargo objects are added to the _DATABASE and SET_CARGO objects.
_EVENTDISPATCHER:CreateEventNewCargo( self )
self:HandleEvent( EVENTS.Dead, self.OnEventCargoDead )
self:HandleEvent( EVENTS.Crash, self.OnEventCargoDead )
--self:HandleEvent( EVENTS.RemoveUnit, self.OnEventCargoDead )
self:HandleEvent( EVENTS.PlayerLeaveUnit, self.OnEventCargoDead )
self:SetEventPriority( 4 )
return self
end
--- Respawn the CargoGroup.
-- @param #CARGO_GROUP self
function CARGO_GROUP:Respawn()
self:T( { "Respawning" } )
for CargoID, CargoData in pairs( self.CargoSet:GetSet() ) do
local Cargo = CargoData -- Cargo.Cargo#CARGO
Cargo:Destroy() -- Destroy the cargo and generate a remove unit event to update the sets.
Cargo:SetStartState( "UnLoaded" )
end
-- Now we spawn the new group based on the template created.
_DATABASE:Spawn( self.GroupTemplate )
for CargoUnitID, CargoUnit in pairs( self.CargoObject:GetUnits() ) do
local CargoUnitName = CargoUnit:GetName()
local Cargo = CARGO_UNIT:New( CargoUnit, self.Type, CargoUnitName, self.LoadRadius )
self.CargoSet:Add( CargoUnitName, Cargo )
end
self:SetDeployed( false )
self:SetStartState( "UnLoaded" )
end
--- Ungroup the cargo group into individual groups with one unit.
-- This is required because by default a group will move in formation and this is really an issue for group control.
-- Therefore this method is made to be able to ungroup a group.
-- This works for ground only groups.
-- @param #CARGO_GROUP self
function CARGO_GROUP:Ungroup()
if self.Grouped == true then
self.Grouped = false
self.CargoGroup:Destroy()
for CargoUnitName, CargoUnit in pairs( self.CargoSet:GetSet() ) do
local CargoUnit = CargoUnit -- Cargo.CargoUnit#CARGO_UNIT
if CargoUnit:IsUnLoaded() then
local GroupTemplate = UTILS.DeepCopy( self.CargoTemplate )
--local GroupName = env.getValueDictByKey( GroupTemplate.name )
-- We create a new group object with one unit...
-- First we prepare the template...
GroupTemplate.name = self.CargoName .. "#CARGO#" .. CargoUnitName
GroupTemplate.groupId = nil
if CargoUnit:IsUnLoaded() then
GroupTemplate.units = {}
GroupTemplate.units[1] = self.CargoUnitTemplate[CargoUnitName]
GroupTemplate.units[#GroupTemplate.units].unitId = nil
GroupTemplate.units[#GroupTemplate.units].x = CargoUnit:GetX()
GroupTemplate.units[#GroupTemplate.units].y = CargoUnit:GetY()
GroupTemplate.units[#GroupTemplate.units].heading = CargoUnit:GetHeading()
end
-- Then we register the new group in the database
local CargoGroup = GROUP:NewTemplate( GroupTemplate, GroupTemplate.CoalitionID, GroupTemplate.CategoryID, GroupTemplate.CountryID)
-- Now we spawn the new group based on the template created.
_DATABASE:Spawn( GroupTemplate )
end
end
self.CargoObject = nil
end
end
--- Regroup the cargo group into one group with multiple unit.
-- This is required because by default a group will move in formation and this is really an issue for group control.
-- Therefore this method is made to be able to regroup a group.
-- This works for ground only groups.
-- @param #CARGO_GROUP self
function CARGO_GROUP:Regroup()
self:T("Regroup")
if self.Grouped == false then
self.Grouped = true
local GroupTemplate = UTILS.DeepCopy( self.CargoTemplate )
GroupTemplate.name = self.CargoName .. "#CARGO"
GroupTemplate.groupId = nil
GroupTemplate.units = {}
for CargoUnitName, CargoUnit in pairs( self.CargoSet:GetSet() ) do
local CargoUnit = CargoUnit -- Cargo.CargoUnit#CARGO_UNIT
self:T( { CargoUnit:GetName(), UnLoaded = CargoUnit:IsUnLoaded() } )
if CargoUnit:IsUnLoaded() then
CargoUnit.CargoObject:Destroy()
GroupTemplate.units[#GroupTemplate.units+1] = self.CargoUnitTemplate[CargoUnitName]
GroupTemplate.units[#GroupTemplate.units].unitId = nil
GroupTemplate.units[#GroupTemplate.units].x = CargoUnit:GetX()
GroupTemplate.units[#GroupTemplate.units].y = CargoUnit:GetY()
GroupTemplate.units[#GroupTemplate.units].heading = CargoUnit:GetHeading()
end
end
-- Then we register the new group in the database
self.CargoGroup = GROUP:NewTemplate( GroupTemplate, GroupTemplate.CoalitionID, GroupTemplate.CategoryID, GroupTemplate.CountryID )
self:T( { "Regroup", GroupTemplate } )
-- Now we spawn the new group based on the template created.
self.CargoObject = _DATABASE:Spawn( GroupTemplate )
end
end
--- @param #CARGO_GROUP self
-- @param Core.Event#EVENTDATA EventData
function CARGO_GROUP:OnEventCargoDead( EventData )
self:T(EventData)
local Destroyed = false
if self:IsDestroyed() or self:IsUnLoaded() or self:IsBoarding() or self:IsUnboarding() then
Destroyed = true
for CargoID, CargoData in pairs( self.CargoSet:GetSet() ) do
local Cargo = CargoData -- Cargo.Cargo#CARGO
if Cargo:IsAlive() then
Destroyed = false
else
Cargo:Destroyed()
end
end
else
local CarrierName = self.CargoCarrier:GetName()
if CarrierName == EventData.IniDCSUnitName then
MESSAGE:New( "Cargo is lost from carrier " .. CarrierName, 15 ):ToAll()
Destroyed = true
self.CargoCarrier:ClearCargo()
end
end
if Destroyed then
self:Destroyed()
self:T( { "Cargo group destroyed" } )
end
end
--- After Board Event.
-- @param #CARGO_GROUP self
-- @param #string Event
-- @param #string From
-- @param #string To
-- @param Wrapper.Unit#UNIT CargoCarrier
-- @param #number NearRadius If distance is smaller than this number, cargo is loaded into the carrier.
function CARGO_GROUP:onafterBoard( From, Event, To, CargoCarrier, NearRadius, ... )
self:T( { CargoCarrier.UnitName, From, Event, To, NearRadius = NearRadius } )
NearRadius = NearRadius or self.NearRadius
-- For each Cargo object within the CARGO_GROUPED, route each object to the CargoLoadPointVec2
self.CargoSet:ForEach(
function( Cargo, ... )
self:T( { "Board Unit", Cargo:GetName( ), Cargo:IsDestroyed(), Cargo.CargoObject:IsAlive() } )
local CargoGroup = Cargo.CargoObject --Wrapper.Group#GROUP
CargoGroup:OptionAlarmStateGreen()
Cargo:__Board( 1, CargoCarrier, NearRadius, ... )
end, ...
)
self:__Boarding( -1, CargoCarrier, NearRadius, ... )
end
--- Enter Loaded State.
-- @param #CARGO_GROUP self
-- @param #string Event
-- @param #string From
-- @param #string To
-- @param Wrapper.Unit#UNIT CargoCarrier
function CARGO_GROUP:onafterLoad( From, Event, To, CargoCarrier, ... )
--self:T( { From, Event, To, CargoCarrier, ...} )
if From == "UnLoaded" then
-- For each Cargo object within the CARGO_GROUP, load each cargo to the CargoCarrier.
for CargoID, Cargo in pairs( self.CargoSet:GetSet() ) do
if not Cargo:IsDestroyed() then
Cargo:Load( CargoCarrier )
end
end
end
--self.CargoObject:Destroy()
self.CargoCarrier = CargoCarrier
self.CargoCarrier:AddCargo( self )
end
--- Leave Boarding State.
-- @param #CARGO_GROUP self
-- @param #string Event
-- @param #string From
-- @param #string To
-- @param Wrapper.Unit#UNIT CargoCarrier
-- @param #number NearRadius If distance is smaller than this number, cargo is loaded into the carrier.
function CARGO_GROUP:onafterBoarding( From, Event, To, CargoCarrier, NearRadius, ... )
--self:T( { CargoCarrier.UnitName, From, Event, To } )
local Boarded = true
local Cancelled = false
local Dead = true
self.CargoSet:Flush()
-- For each Cargo object within the CARGO_GROUP, route each object to the CargoLoadPointVec2
for CargoID, Cargo in pairs( self.CargoSet:GetSet() ) do
--self:T( { Cargo:GetName(), Cargo.current } )
if not Cargo:is( "Loaded" )
and (not Cargo:is( "Destroyed" )) then -- If one or more units of a group defined as CARGO_GROUP died, the CARGO_GROUP:Board() command does not trigger the CARGO_GRUOP:OnEnterLoaded() function.
Boarded = false
end
if Cargo:is( "UnLoaded" ) then
Cancelled = true
end
if not Cargo:is( "Destroyed" ) then
Dead = false
end
end
if not Dead then
if not Cancelled then
if not Boarded then
self:__Boarding( -5, CargoCarrier, NearRadius, ... )
else
self:T("Group Cargo is loaded")
self:__Load( 1, CargoCarrier, ... )
end
else
self:__CancelBoarding( 1, CargoCarrier, NearRadius, ... )
end
else
self:__Destroyed( 1, CargoCarrier, NearRadius, ... )
end
end
--- Enter UnBoarding State.
-- @param #CARGO_GROUP self
-- @param #string Event
-- @param #string From
-- @param #string To
-- @param Core.Point#POINT_VEC2 ToPointVec2
-- @param #number NearRadius If distance is smaller than this number, cargo is loaded into the carrier.
function CARGO_GROUP:onafterUnBoard( From, Event, To, ToPointVec2, NearRadius, ... )
self:T( {From, Event, To, ToPointVec2, NearRadius } )
NearRadius = NearRadius or 25
local Timer = 1
if From == "Loaded" then
if self.CargoObject then
self.CargoObject:Destroy()
end
-- For each Cargo object within the CARGO_GROUP, route each object to the CargoLoadPointVec2
self.CargoSet:ForEach(
--- @param Cargo.Cargo#CARGO Cargo
function( Cargo, NearRadius )
if not Cargo:IsDestroyed() then
local ToVec=nil
if ToPointVec2==nil then
ToVec=self.CargoCarrier:GetPointVec2():GetRandomPointVec2InRadius(2*NearRadius, NearRadius)
else
ToVec=ToPointVec2
end
Cargo:__UnBoard( Timer, ToVec, NearRadius )
Timer = Timer + 1
end
end, { NearRadius }
)
self:__UnBoarding( 1, ToPointVec2, NearRadius, ... )
end
end
--- Leave UnBoarding State.
-- @param #CARGO_GROUP self
-- @param #string Event
-- @param #string From
-- @param #string To
-- @param Core.Point#POINT_VEC2 ToPointVec2
-- @param #number NearRadius If distance is smaller than this number, cargo is loaded into the carrier.
function CARGO_GROUP:onafterUnBoarding( From, Event, To, ToPointVec2, NearRadius, ... )
--self:T( { From, Event, To, ToPointVec2, NearRadius } )
--local NearRadius = NearRadius or 25
local Angle = 180
local Speed = 10
local Distance = 5
if From == "UnBoarding" then
local UnBoarded = true
-- For each Cargo object within the CARGO_GROUP, route each object to the CargoLoadPointVec2
for CargoID, Cargo in pairs( self.CargoSet:GetSet() ) do
self:T( { Cargo:GetName(), Cargo.current } )
if not Cargo:is( "UnLoaded" ) and not Cargo:IsDestroyed() then
UnBoarded = false
end
end
if UnBoarded then
self:__UnLoad( 1, ToPointVec2, ... )
else
self:__UnBoarding( 1, ToPointVec2, NearRadius, ... )
end
return false
end
end
--- Enter UnLoaded State.
-- @param #CARGO_GROUP self
-- @param #string Event
-- @param #string From
-- @param #string To
-- @param Core.Point#POINT_VEC2 ToPointVec2
function CARGO_GROUP:onafterUnLoad( From, Event, To, ToPointVec2, ... )
--self:T( { From, Event, To, ToPointVec2 } )
if From == "Loaded" then
-- For each Cargo object within the CARGO_GROUP, route each object to the CargoLoadPointVec2
self.CargoSet:ForEach(
function( Cargo )
--Cargo:UnLoad( ToPointVec2 )
local RandomVec2=nil
if ToPointVec2 then
RandomVec2=ToPointVec2:GetRandomPointVec2InRadius(20, 10)
end
Cargo:UnBoard( RandomVec2 )
end
)
end
self.CargoCarrier:RemoveCargo( self )
self.CargoCarrier = nil
end
--- Get the current Coordinate of the CargoGroup.
-- @param #CARGO_GROUP self
-- @return Core.Point#COORDINATE The current Coordinate of the first Cargo of the CargoGroup.
-- @return #nil There is no valid Cargo in the CargoGroup.
function CARGO_GROUP:GetCoordinate()
local Cargo = self:GetFirstAlive() -- Cargo.Cargo#CARGO
if Cargo then
return Cargo.CargoObject:GetCoordinate()
end
return nil
end
--- Get the x position of the cargo.
-- @param #CARGO_GROUP self
-- @return #number
function CARGO:GetX()
local Cargo = self:GetFirstAlive() -- Cargo.Cargo#CARGO
if Cargo then
return Cargo:GetCoordinate().x
end
return nil
end
--- Get the y position of the cargo.
-- @param #CARGO_GROUP self
-- @return #number
function CARGO:GetY()
local Cargo = self:GetFirstAlive() -- Cargo.Cargo#CARGO
if Cargo then
return Cargo:GetCoordinate().z
end
return nil
end
--- Check if the CargoGroup is alive.
-- @param #CARGO_GROUP self
-- @return #boolean true if the CargoGroup is alive.
-- @return #boolean false if the CargoGroup is dead.
function CARGO_GROUP:IsAlive()
local Cargo = self:GetFirstAlive() -- Cargo.Cargo#CARGO
return Cargo ~= nil
end
--- Get the first alive Cargo Unit of the Cargo Group.
-- @param #CARGO_GROUP self
-- @return #CARGO_GROUP
function CARGO_GROUP:GetFirstAlive()
local CargoFirstAlive = nil
for _, Cargo in pairs( self.CargoSet:GetSet() ) do
if not Cargo:IsDestroyed() then
CargoFirstAlive = Cargo
break
end
end
return CargoFirstAlive
end
--- Get the amount of cargo units in the group.
-- @param #CARGO_GROUP self
-- @return #CARGO_GROUP
function CARGO_GROUP:GetCount()
return self.CargoSet:Count()
end
--- Get the underlying GROUP object from the CARGO_GROUP.
-- @param #CARGO_GROUP self
-- @return #CARGO_GROUP
function CARGO_GROUP:GetGroup( Cargo )
local Cargo = Cargo or self:GetFirstAlive() -- Cargo.Cargo#CARGO
return Cargo.CargoObject:GetGroup()
end
--- Route Cargo to Coordinate and randomize locations.
-- @param #CARGO_GROUP self
-- @param Core.Point#COORDINATE Coordinate
function CARGO_GROUP:RouteTo( Coordinate )
--self:T( {Coordinate = Coordinate } )
-- For each Cargo within the CargoSet, route each object to the Coordinate
self.CargoSet:ForEach(
function( Cargo )
Cargo.CargoObject:RouteGroundTo( Coordinate, 10, "vee", 0 )
end
)
end
--- Check if Cargo is near to the Carrier.
-- The Cargo is near to the Carrier if the first unit of the Cargo Group is within NearRadius.
-- @param #CARGO_GROUP self
-- @param Wrapper.Group#GROUP CargoCarrier
-- @param #number NearRadius
-- @return #boolean The Cargo is near to the Carrier or #nil if the Cargo is not near to the Carrier.
function CARGO_GROUP:IsNear( CargoCarrier, NearRadius )
self:T( {NearRadius = NearRadius } )
for _, Cargo in pairs( self.CargoSet:GetSet() ) do
local Cargo = Cargo -- Cargo.Cargo#CARGO
if Cargo:IsAlive() then
if Cargo:IsNear( CargoCarrier:GetCoordinate(), NearRadius ) then
self:T( "Near" )
return true
end
end
end
return nil
end
--- Check if Cargo Group is in the radius for the Cargo to be Boarded.
-- @param #CARGO_GROUP self
-- @param Core.Point#COORDINATE Coordinate
-- @return #boolean true if the Cargo Group is within the load radius.
function CARGO_GROUP:IsInLoadRadius( Coordinate )
--self:T( { Coordinate } )
local Cargo = self:GetFirstAlive() -- Cargo.Cargo#CARGO
if Cargo then
local Distance = 0
local CargoCoordinate
if Cargo:IsLoaded() then
CargoCoordinate = Cargo.CargoCarrier:GetCoordinate()
else
CargoCoordinate = Cargo.CargoObject:GetCoordinate()
end
-- FF check if coordinate could be obtained. This was commented out for some (unknown) reason. But the check seems valid!
if CargoCoordinate then
Distance = Coordinate:Get2DDistance( CargoCoordinate )
else
return false
end
self:T( { Distance = Distance, LoadRadius = self.LoadRadius } )
if Distance <= self.LoadRadius then
return true
else
return false
end
end
return nil
end
--- Check if Cargo Group is in the report radius.
-- @param #CARGO_GROUP self
-- @param Core.Point#Coordinate Coordinate
-- @return #boolean true if the Cargo Group is within the report radius.
function CARGO_GROUP:IsInReportRadius( Coordinate )
--self:T( { Coordinate } )
local Cargo = self:GetFirstAlive() -- Cargo.Cargo#CARGO
if Cargo then
self:T( { Cargo } )
local Distance = 0
if Cargo:IsUnLoaded() then
Distance = Coordinate:Get2DDistance( Cargo.CargoObject:GetCoordinate() )
--self:T( Distance )
if Distance <= self.LoadRadius then
return true
end
end
end
return nil
end
--- Signal a flare at the position of the CargoGroup.
-- @param #CARGO_GROUP self
-- @param Utilities.Utils#FLARECOLOR FlareColor
function CARGO_GROUP:Flare( FlareColor )
local Cargo = self.CargoSet:GetFirst() -- Cargo.Cargo#CARGO
if Cargo then
Cargo:Flare( FlareColor )
end
end
--- Smoke the CargoGroup.
-- @param #CARGO_GROUP self
-- @param Utilities.Utils#SMOKECOLOR SmokeColor The color of the smoke.
-- @param #number Radius The radius of randomization around the center of the first element of the CargoGroup.
function CARGO_GROUP:Smoke( SmokeColor, Radius )
local Cargo = self.CargoSet:GetFirst() -- Cargo.Cargo#CARGO
if Cargo then
Cargo:Smoke( SmokeColor, Radius )
end
end
--- Check if the first element of the CargoGroup is the given @{Core.Zone}.
-- @param #CARGO_GROUP self
-- @param Core.Zone#ZONE_BASE Zone
-- @return #boolean **true** if the first element of the CargoGroup is in the Zone
-- @return #boolean **false** if there is no element of the CargoGroup in the Zone.
function CARGO_GROUP:IsInZone( Zone )
--self:T( { Zone } )
local Cargo = self.CargoSet:GetFirst() -- Cargo.Cargo#CARGO
if Cargo then
return Cargo:IsInZone( Zone )
end
return nil
end
--- Get the transportation method of the Cargo.
-- @param #CARGO_GROUP self
-- @return #string The transportation method of the Cargo.
function CARGO_GROUP:GetTransportationMethod()
if self:IsLoaded() then
return "for unboarding"
else
if self:IsUnLoaded() then
return "for boarding"
else
if self:IsDeployed() then
return "delivered"
end
end
end
return ""
end
end -- CARGO_GROUP

View File

@@ -1,275 +0,0 @@
--- **Cargo** - Management of single cargo crates, which are based on a STATIC object. The cargo can only be slingloaded.
--
-- ===
--
-- ### [Demo Missions]()
--
-- ### [YouTube Playlist]()
--
-- ===
--
-- ### Author: **FlightControl**
-- ### Contributions:
--
-- ===
--
-- @module Cargo.CargoSlingload
-- @image Cargo_Slingload.JPG
do -- CARGO_SLINGLOAD
--- Models the behaviour of cargo crates, which can only be slingloaded.
-- @type CARGO_SLINGLOAD
-- @extends Cargo.Cargo#CARGO_REPRESENTABLE
--- Defines a cargo that is represented by a UNIT object within the simulator, and can be transported by a carrier.
--
-- The above cargo classes are also used by the TASK_CARGO_ classes to allow human players to transport cargo as part of a tasking:
--
-- * @{Tasking.Task_Cargo_Transport#TASK_CARGO_TRANSPORT} to transport cargo by human players.
-- * @{Tasking.Task_Cargo_Transport#TASK_CARGO_CSAR} to transport downed pilots by human players.
--
-- # Developer Note
--
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
-- Therefore, this class is considered to be deprecated
--
-- ===
--
-- @field #CARGO_SLINGLOAD
CARGO_SLINGLOAD = {
ClassName = "CARGO_SLINGLOAD"
}
--- CARGO_SLINGLOAD Constructor.
-- @param #CARGO_SLINGLOAD self
-- @param Wrapper.Static#STATIC CargoStatic
-- @param #string Type
-- @param #string Name
-- @param #number LoadRadius (optional)
-- @param #number NearRadius (optional)
-- @return #CARGO_SLINGLOAD
function CARGO_SLINGLOAD:New( CargoStatic, Type, Name, LoadRadius, NearRadius )
local self = BASE:Inherit( self, CARGO_REPRESENTABLE:New( CargoStatic, Type, Name, nil, LoadRadius, NearRadius ) ) -- #CARGO_SLINGLOAD
self:T( { Type, Name, NearRadius } )
self.CargoObject = CargoStatic
-- Cargo objects are added to the _DATABASE and SET_CARGO objects.
_EVENTDISPATCHER:CreateEventNewCargo( self )
self:HandleEvent( EVENTS.Dead, self.OnEventCargoDead )
self:HandleEvent( EVENTS.Crash, self.OnEventCargoDead )
--self:HandleEvent( EVENTS.RemoveUnit, self.OnEventCargoDead )
self:HandleEvent( EVENTS.PlayerLeaveUnit, self.OnEventCargoDead )
self:SetEventPriority( 4 )
self.NearRadius = NearRadius or 25
return self
end
-- @param #CARGO_SLINGLOAD self
-- @param Core.Event#EVENTDATA EventData
function CARGO_SLINGLOAD:OnEventCargoDead( EventData )
local Destroyed = false
if self:IsDestroyed() or self:IsUnLoaded() then
if self.CargoObject:GetName() == EventData.IniUnitName then
if not self.NoDestroy then
Destroyed = true
end
end
end
if Destroyed then
self:I( { "Cargo crate destroyed: " .. self.CargoObject:GetName() } )
self:Destroyed()
end
end
--- Check if the cargo can be Slingloaded.
-- @param #CARGO_SLINGLOAD self
function CARGO_SLINGLOAD:CanSlingload()
return true
end
--- Check if the cargo can be Boarded.
-- @param #CARGO_SLINGLOAD self
function CARGO_SLINGLOAD:CanBoard()
return false
end
--- Check if the cargo can be Unboarded.
-- @param #CARGO_SLINGLOAD self
function CARGO_SLINGLOAD:CanUnboard()
return false
end
--- Check if the cargo can be Loaded.
-- @param #CARGO_SLINGLOAD self
function CARGO_SLINGLOAD:CanLoad()
return false
end
--- Check if the cargo can be Unloaded.
-- @param #CARGO_SLINGLOAD self
function CARGO_SLINGLOAD:CanUnload()
return false
end
--- Check if Cargo Crate is in the radius for the Cargo to be reported.
-- @param #CARGO_SLINGLOAD self
-- @param Core.Point#COORDINATE Coordinate
-- @return #boolean true if the Cargo Crate is within the report radius.
function CARGO_SLINGLOAD:IsInReportRadius( Coordinate )
--self:T( { Coordinate, LoadRadius = self.LoadRadius } )
local Distance = 0
if self:IsUnLoaded() then
Distance = Coordinate:Get2DDistance( self.CargoObject:GetCoordinate() )
if Distance <= self.LoadRadius then
return true
end
end
return false
end
--- Check if Cargo Slingload is in the radius for the Cargo to be Boarded or Loaded.
-- @param #CARGO_SLINGLOAD self
-- @param Core.Point#COORDINATE Coordinate
-- @return #boolean true if the Cargo Slingload is within the loading radius.
function CARGO_SLINGLOAD:IsInLoadRadius( Coordinate )
--self:T( { Coordinate } )
local Distance = 0
if self:IsUnLoaded() then
Distance = Coordinate:Get2DDistance( self.CargoObject:GetCoordinate() )
if Distance <= self.NearRadius then
return true
end
end
return false
end
--- Get the current Coordinate of the CargoGroup.
-- @param #CARGO_SLINGLOAD self
-- @return Core.Point#COORDINATE The current Coordinate of the first Cargo of the CargoGroup.
-- @return #nil There is no valid Cargo in the CargoGroup.
function CARGO_SLINGLOAD:GetCoordinate()
--self:T()
return self.CargoObject:GetCoordinate()
end
--- Check if the CargoGroup is alive.
-- @param #CARGO_SLINGLOAD self
-- @return #boolean true if the CargoGroup is alive.
-- @return #boolean false if the CargoGroup is dead.
function CARGO_SLINGLOAD:IsAlive()
local Alive = true
-- When the Cargo is Loaded, the Cargo is in the CargoCarrier, so we check if the CargoCarrier is alive.
-- When the Cargo is not Loaded, the Cargo is the CargoObject, so we check if the CargoObject is alive.
if self:IsLoaded() then
Alive = Alive == true and self.CargoCarrier:IsAlive()
else
Alive = Alive == true and self.CargoObject:IsAlive()
end
return Alive
end
--- Route Cargo to Coordinate and randomize locations.
-- @param #CARGO_SLINGLOAD self
-- @param Core.Point#COORDINATE Coordinate
function CARGO_SLINGLOAD:RouteTo( Coordinate )
--self:T( {Coordinate = Coordinate } )
end
--- Check if Cargo is near to the Carrier.
-- The Cargo is near to the Carrier within NearRadius.
-- @param #CARGO_SLINGLOAD self
-- @param Wrapper.Group#GROUP CargoCarrier
-- @param #number NearRadius
-- @return #boolean The Cargo is near to the Carrier.
-- @return #nil The Cargo is not near to the Carrier.
function CARGO_SLINGLOAD:IsNear( CargoCarrier, NearRadius )
--self:T( {NearRadius = NearRadius } )
return self:IsNear( CargoCarrier:GetCoordinate(), NearRadius )
end
--- Respawn the CargoGroup.
-- @param #CARGO_SLINGLOAD self
function CARGO_SLINGLOAD:Respawn()
--self:T( { "Respawning slingload " .. self:GetName() } )
-- Respawn the group...
if self.CargoObject then
self.CargoObject:ReSpawn() -- A cargo destroy crates a DEAD event.
self:__Reset( -0.1 )
end
end
--- Respawn the CargoGroup.
-- @param #CARGO_SLINGLOAD self
function CARGO_SLINGLOAD:onafterReset()
--self:T( { "Reset slingload " .. self:GetName() } )
-- Respawn the group...
if self.CargoObject then
self:SetDeployed( false )
self:SetStartState( "UnLoaded" )
self.CargoCarrier = nil
-- Cargo objects are added to the _DATABASE and SET_CARGO objects.
_EVENTDISPATCHER:CreateEventNewCargo( self )
end
end
--- Get the transportation method of the Cargo.
-- @param #CARGO_SLINGLOAD self
-- @return #string The transportation method of the Cargo.
function CARGO_SLINGLOAD:GetTransportationMethod()
if self:IsLoaded() then
return "for sling loading"
else
if self:IsUnLoaded() then
return "for sling loading"
else
if self:IsDeployed() then
return "delivered"
end
end
end
return ""
end
end

View File

@@ -1,395 +0,0 @@
--- **Cargo** - Management of single cargo logistics, which are based on a UNIT object.
--
-- ===
--
-- ### [Demo Missions]()
--
-- ### [YouTube Playlist]()
--
-- ===
--
-- ### Author: **FlightControl**
-- ### Contributions:
--
-- ===
--
-- @module Cargo.CargoUnit
-- @image Cargo_Units.JPG
do -- CARGO_UNIT
--- Models CARGO in the form of units, which can be boarded, unboarded, loaded, unloaded.
-- @type CARGO_UNIT
-- @extends Cargo.Cargo#CARGO_REPRESENTABLE
--- Defines a cargo that is represented by a UNIT object within the simulator, and can be transported by a carrier.
-- Use the event functions as described above to Load, UnLoad, Board, UnBoard the CARGO_UNIT objects to and from carriers.
-- Note that ground forces behave in a group, and thus, act in formation, regardless if one unit is commanded to move.
--
-- This class is used in CARGO_GROUP, and is not meant to be used by mission designers individually.
--
-- # Developer Note
--
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
-- Therefore, this class is considered to be deprecated
--
-- ===
--
-- @field #CARGO_UNIT CARGO_UNIT
--
CARGO_UNIT = {
ClassName = "CARGO_UNIT"
}
--- CARGO_UNIT Constructor.
-- @param #CARGO_UNIT self
-- @param Wrapper.Unit#UNIT CargoUnit
-- @param #string Type
-- @param #string Name
-- @param #number Weight
-- @param #number LoadRadius (optional)
-- @param #number NearRadius (optional)
-- @return #CARGO_UNIT
function CARGO_UNIT:New( CargoUnit, Type, Name, LoadRadius, NearRadius )
-- Inherit CARGO_REPRESENTABLE.
local self = BASE:Inherit( self, CARGO_REPRESENTABLE:New( CargoUnit, Type, Name, LoadRadius, NearRadius ) ) -- #CARGO_UNIT
-- Debug info.
self:T({Type=Type, Name=Name, LoadRadius=LoadRadius, NearRadius=NearRadius})
-- Set cargo object.
self.CargoObject = CargoUnit
-- Set event prio.
self:SetEventPriority( 5 )
return self
end
--- Enter UnBoarding State.
-- @param #CARGO_UNIT self
-- @param #string Event
-- @param #string From
-- @param #string To
-- @param Core.Point#POINT_VEC2 ToPointVec2
-- @param #number NearRadius (optional) Defaut 25 m.
function CARGO_UNIT:onenterUnBoarding( From, Event, To, ToPointVec2, NearRadius )
self:T( { From, Event, To, ToPointVec2, NearRadius } )
local Angle = 180
local Speed = 60
local DeployDistance = 9
local RouteDistance = 60
if From == "Loaded" then
if not self:IsDestroyed() then
local CargoCarrier = self.CargoCarrier -- Wrapper.Controllable#CONTROLLABLE
if CargoCarrier:IsAlive() then
local CargoCarrierPointVec2 = CargoCarrier:GetPointVec2()
local CargoCarrierHeading = self.CargoCarrier:GetHeading() -- Get Heading of object in degrees.
local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle )
local CargoRoutePointVec2 = CargoCarrierPointVec2:Translate( RouteDistance, CargoDeployHeading )
-- if there is no ToPointVec2 given, then use the CargoRoutePointVec2
local FromDirectionVec3 = CargoCarrierPointVec2:GetDirectionVec3( ToPointVec2 or CargoRoutePointVec2 )
local FromAngle = CargoCarrierPointVec2:GetAngleDegrees(FromDirectionVec3)
local FromPointVec2 = CargoCarrierPointVec2:Translate( DeployDistance, FromAngle )
--local CargoDeployPointVec2 = CargoCarrierPointVec2:GetRandomCoordinateInRadius( 10, 5 )
ToPointVec2 = ToPointVec2 or CargoCarrierPointVec2:GetRandomCoordinateInRadius( NearRadius, DeployDistance )
-- Respawn the group...
if self.CargoObject then
if CargoCarrier:IsShip() then
-- If CargoCarrier is a ship, we don't want to spawn the units in the water next to the boat. Use destination coord instead.
self.CargoObject:ReSpawnAt( ToPointVec2, CargoDeployHeading )
else
self.CargoObject:ReSpawnAt( FromPointVec2, CargoDeployHeading )
end
self:T( { "CargoUnits:", self.CargoObject:GetGroup():GetName() } )
self.CargoCarrier = nil
local Points = {}
-- From
Points[#Points+1] = FromPointVec2:WaypointGround( Speed, "Vee" )
-- To
Points[#Points+1] = ToPointVec2:WaypointGround( Speed, "Vee" )
local TaskRoute = self.CargoObject:TaskRoute( Points )
self.CargoObject:SetTask( TaskRoute, 1 )
self:__UnBoarding( 1, ToPointVec2, NearRadius )
end
else
-- the Carrier is dead. This cargo is dead too!
self:Destroyed()
end
end
end
end
--- Leave UnBoarding State.
-- @param #CARGO_UNIT self
-- @param #string Event
-- @param #string From
-- @param #string To
-- @param Core.Point#POINT_VEC2 ToPointVec2
-- @param #number NearRadius (optional) Defaut 100 m.
function CARGO_UNIT:onleaveUnBoarding( From, Event, To, ToPointVec2, NearRadius )
self:T( { From, Event, To, ToPointVec2, NearRadius } )
local Angle = 180
local Speed = 10
local Distance = 5
if From == "UnBoarding" then
--if self:IsNear( ToPointVec2, NearRadius ) then
return true
--else
--self:__UnBoarding( 1, ToPointVec2, NearRadius )
--end
--return false
end
end
--- UnBoard Event.
-- @param #CARGO_UNIT self
-- @param #string Event
-- @param #string From
-- @param #string To
-- @param Core.Point#POINT_VEC2 ToPointVec2
-- @param #number NearRadius (optional) Defaut 100 m.
function CARGO_UNIT:onafterUnBoarding( From, Event, To, ToPointVec2, NearRadius )
self:T( { From, Event, To, ToPointVec2, NearRadius } )
self.CargoInAir = self.CargoObject:InAir()
self:T( self.CargoInAir )
-- Only unboard the cargo when the carrier is not in the air.
-- (eg. cargo can be on a oil derrick, moving the cargo on the oil derrick will drop the cargo on the sea).
if not self.CargoInAir then
end
self:__UnLoad( 1, ToPointVec2, NearRadius )
end
--- Enter UnLoaded State.
-- @param #CARGO_UNIT self
-- @param #string Event
-- @param #string From
-- @param #string To
-- @param Core.Point#POINT_VEC2
function CARGO_UNIT:onenterUnLoaded( From, Event, To, ToPointVec2 )
self:T( { ToPointVec2, From, Event, To } )
local Angle = 180
local Speed = 10
local Distance = 5
if From == "Loaded" then
local StartPointVec2 = self.CargoCarrier:GetPointVec2()
local CargoCarrierHeading = self.CargoCarrier:GetHeading() -- Get Heading of object in degrees.
local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle )
local CargoDeployCoord = StartPointVec2:Translate( Distance, CargoDeployHeading )
ToPointVec2 = ToPointVec2 or COORDINATE:New( CargoDeployCoord.x, CargoDeployCoord.z )
-- Respawn the group...
if self.CargoObject then
self.CargoObject:ReSpawnAt( ToPointVec2, 0 )
self.CargoCarrier = nil
end
end
if self.OnUnLoadedCallBack then
self.OnUnLoadedCallBack( self, unpack( self.OnUnLoadedParameters ) )
self.OnUnLoadedCallBack = nil
end
end
--- Board Event.
-- @param #CARGO_UNIT self
-- @param #string Event
-- @param #string From
-- @param #string To
-- @param Wrapper.Group#GROUP CargoCarrier
-- @param #number NearRadius
function CARGO_UNIT:onafterBoard( From, Event, To, CargoCarrier, NearRadius, ... )
self:T( { From, Event, To, CargoCarrier, NearRadius = NearRadius } )
self.CargoInAir = self.CargoObject:InAir()
local Desc = self.CargoObject:GetDesc()
local MaxSpeed = Desc.speedMaxOffRoad
local TypeName = Desc.typeName
--self:T({Unit=self.CargoObject:GetName()})
-- A cargo unit can only be boarded if it is not dead
-- Only move the group to the carrier when the cargo is not in the air
-- (eg. cargo can be on a oil derrick, moving the cargo on the oil derrick will drop the cargo on the sea).
if not self.CargoInAir then
-- If NearRadius is given, then use the given NearRadius, otherwise calculate the NearRadius
-- based upon the Carrier bounding radius, which is calculated from the bounding rectangle on the Y axis.
local NearRadius = NearRadius or CargoCarrier:GetBoundingRadius() + 5
if self:IsNear( CargoCarrier:GetPointVec2(), NearRadius ) then
self:Load( CargoCarrier, NearRadius, ... )
else
if MaxSpeed and MaxSpeed == 0 or TypeName and TypeName == "Stinger comm" then
self:Load( CargoCarrier, NearRadius, ... )
else
local Speed = 90
local Angle = 180
local Distance = 0
local CargoCarrierPointVec2 = CargoCarrier:GetPointVec2()
local CargoCarrierHeading = CargoCarrier:GetHeading() -- Get Heading of object in degrees.
local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle )
local CargoDeployPointVec2 = CargoCarrierPointVec2:Translate( Distance, CargoDeployHeading )
-- Set the CargoObject to state Green to ensure it is boarding!
self.CargoObject:OptionAlarmStateGreen()
local Points = {}
local PointStartVec2 = self.CargoObject:GetPointVec2()
Points[#Points+1] = PointStartVec2:WaypointGround( Speed )
Points[#Points+1] = CargoDeployPointVec2:WaypointGround( Speed )
local TaskRoute = self.CargoObject:TaskRoute( Points )
self.CargoObject:SetTask( TaskRoute, 2 )
self:__Boarding( -5, CargoCarrier, NearRadius, ... )
self.RunCount = 0
end
end
end
end
--- Boarding Event.
-- @param #CARGO_UNIT self
-- @param #string Event
-- @param #string From
-- @param #string To
-- @param Wrapper.Client#CLIENT CargoCarrier
-- @param #number NearRadius Default 25 m.
function CARGO_UNIT:onafterBoarding( From, Event, To, CargoCarrier, NearRadius, ... )
self:T( { From, Event, To, CargoCarrier:GetName(), NearRadius = NearRadius } )
self:T( { IsAlive=self.CargoObject:IsAlive() } )
if CargoCarrier and CargoCarrier:IsAlive() then -- and self.CargoObject and self.CargoObject:IsAlive() then
if (CargoCarrier:IsAir() and not CargoCarrier:InAir()) or true then
local NearRadius = NearRadius or CargoCarrier:GetBoundingRadius( NearRadius ) + 5
if self:IsNear( CargoCarrier:GetPointVec2(), NearRadius ) then
self:__Load( -1, CargoCarrier, ... )
else
if self:IsNear( CargoCarrier:GetPointVec2(), 20 ) then
self:__Boarding( -1, CargoCarrier, NearRadius, ... )
self.RunCount = self.RunCount + 1
else
self:__Boarding( -2, CargoCarrier, NearRadius, ... )
self.RunCount = self.RunCount + 2
end
if self.RunCount >= 40 then
self.RunCount = 0
local Speed = 90
local Angle = 180
local Distance = 0
--self:T({Unit=self.CargoObject:GetName()})
local CargoCarrierPointVec2 = CargoCarrier:GetPointVec2()
local CargoCarrierHeading = CargoCarrier:GetHeading() -- Get Heading of object in degrees.
local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle )
local CargoDeployPointVec2 = CargoCarrierPointVec2:Translate( Distance, CargoDeployHeading )
-- Set the CargoObject to state Green to ensure it is boarding!
self.CargoObject:OptionAlarmStateGreen()
local Points = {}
local PointStartVec2 = self.CargoObject:GetPointVec2()
Points[#Points+1] = PointStartVec2:WaypointGround( Speed, "Off road" )
Points[#Points+1] = CargoDeployPointVec2:WaypointGround( Speed, "Off road" )
local TaskRoute = self.CargoObject:TaskRoute( Points )
self.CargoObject:SetTask( TaskRoute, 0.2 )
end
end
else
self.CargoObject:MessageToGroup( "Cancelling Boarding... Get back on the ground!", 5, CargoCarrier:GetGroup(), self:GetName() )
self:CancelBoarding( CargoCarrier, NearRadius, ... )
self.CargoObject:SetCommand( self.CargoObject:CommandStopRoute( true ) )
end
else
self:T("Something is wrong")
end
end
--- Loaded State.
-- @param #CARGO_UNIT self
-- @param #string Event
-- @param #string From
-- @param #string To
-- @param Wrapper.Unit#UNIT CargoCarrier
function CARGO_UNIT:onenterLoaded( From, Event, To, CargoCarrier )
self:T( { From, Event, To, CargoCarrier } )
self.CargoCarrier = CargoCarrier
--self:T({Unit=self.CargoObject:GetName()})
-- Only destroy the CargoObject if there is a CargoObject (packages don't have CargoObjects).
if self.CargoObject then
self.CargoObject:Destroy( false )
--self.CargoObject:ReSpawnAt( COORDINATE:NewFromVec2( {x=0,y=0} ), 0 )
end
end
--- Get the transportation method of the Cargo.
-- @param #CARGO_UNIT self
-- @return #string The transportation method of the Cargo.
function CARGO_UNIT:GetTransportationMethod()
if self:IsLoaded() then
return "for unboarding"
else
if self:IsUnLoaded() then
return "for boarding"
else
if self:IsDeployed() then
return "delivered"
end
end
end
return ""
end
end -- CARGO_UNIT

View File

@@ -34,11 +34,12 @@ local _TraceClassMethod = {}
local _ClassID = 0
---
--- Base class of everything
-- @type BASE
-- @field ClassName The name of the class.
-- @field ClassID The ID number of the class.
-- @field ClassNameAndID The name of the class concatenated with the ID number of the class.
-- @field #string ClassName The name of the class.
-- @field #number ClassID The ID number of the class.
-- @field #string ClassNameAndID The name of the class concatenated with the ID number of the class.
-- @field Core.Scheduler#SCHEDULER Scheduler The scheduler object.
--- BASE class
--
@@ -200,6 +201,7 @@ BASE = {
States = {},
Debug = debug,
Scheduler = nil,
Properties = {},
}
-- @field #BASE.__
@@ -210,14 +212,6 @@ BASE._ = {
Schedules = {}, --- Contains the Schedulers Active
}
--- The Formation Class
-- @type FORMATION
-- @field Cone A cone formation.
FORMATION = {
Cone = "Cone",
Vee = "Vee",
}
--- BASE constructor.
--
-- This is an example how to use the BASE:New() constructor in a new class definition when inheriting from BASE.
@@ -980,7 +974,7 @@ do -- Scheduling
-- @param #BASE self
-- @param #number Start Specifies the amount of seconds that will be waited before the scheduling is started, and the event function is called.
-- @param #function SchedulerFunction The event function to be called when a timer event occurs. The event function needs to accept the parameters specified in SchedulerArguments.
-- @param #table ... Optional arguments that can be given as part of scheduler. The arguments need to be given as a table { param1, param 2, ... }.
-- @param ... Optional arguments that can be given as part of scheduler. The arguments need to be given as a table { param1, param 2, ... }.
-- @return #string The Schedule ID of the planned schedule.
function BASE:ScheduleOnce( Start, SchedulerFunction, ... )
@@ -1116,6 +1110,31 @@ function BASE:ClearState( Object, StateName )
end
end
--- Set one property of an object.
-- @param #BASE self
-- @param Key The key that is used as a reference of the value. Note that the key can be a #string, but it can also be any other type!
-- @param Value The value that is stored. Note that the value can be a #string, but it can also be any other type!
function BASE:SetProperty(Key,Value)
self.Properties = self.Properties or {}
self.Properties[Key] = Value
end
--- Get one property of an object by the key.
-- @param #BASE self
-- @param Key The key that is used as a reference of the value. Note that the key can be a #string, but it can also be any other type!
-- @return Value The value that is stored. Note that the value can be a #string, but it can also be any other type! Nil if not found.
function BASE:GetProperty(Key)
self.Properties = self.Properties or {}
return self.Properties[Key]
end
--- Get all of the properties of an object in a table.
-- @param #BASE self
-- @return #table of values, indexed by keys.
function BASE:GetProperties()
return self.Properties
end
-- Trace section
-- Log a trace (only shown when trace is on)
@@ -1416,7 +1435,7 @@ function BASE:E( Arguments )
env.info( string.format( "%6d(%6d)/%1s:%30s%05d.%s(%s)", LineCurrent, LineFrom, "E", self.ClassName, self.ClassID, Function, UTILS.BasicSerialize( Arguments ) ) )
else
env.info( string.format( "%1s:%30s%05d(%s)", "E", self.ClassName, self.ClassID, BASE:_Serialize(Arguments) ) )
env.info( string.format( "%1s:%30s%05d(%s)", "E", self.ClassName, self.ClassID, UTILS.BasicSerialize(Arguments) ) )
end
end
@@ -1443,8 +1462,7 @@ function BASE:I( Arguments )
env.info( string.format( "%6d(%6d)/%1s:%30s%05d.%s(%s)", LineCurrent, LineFrom, "I", self.ClassName, self.ClassID, Function, UTILS.BasicSerialize( Arguments ) ) )
else
env.info( string.format( "%1s:%30s%05d(%s)", "I", self.ClassName, self.ClassID, BASE:_Serialize(Arguments)) )
env.info( string.format( "%1s:%30s%05d(%s)", "I", self.ClassName, self.ClassID, UTILS.BasicSerialize(Arguments)) )
end
end

View File

@@ -20,7 +20,7 @@
--
-- @module Core.ClientMenu
-- @image Core_Menu.JPG
-- last change: May 2024
-- last change: Jan 2025
-- TODO
----------------------------------------------------------------------------------------------------------------
@@ -57,9 +57,9 @@
---
-- @field #CLIENTMENU
CLIENTMENU = {
ClassName = "CLIENTMENUE",
ClassName = "CLIENTMENU",
lid = "",
version = "0.1.2",
version = "0.1.3",
name = nil,
path = nil,
group = nil,
@@ -455,7 +455,7 @@ end
-- @param #CLIENTMENUMANAGER self
-- @param Core.Event#EVENTDATA EventData
-- @return #CLIENTMENUMANAGER self
function CLIENTMENUMANAGER:_EventHandler(EventData)
function CLIENTMENUMANAGER:_EventHandler(EventData,Retry)
self:T(self.lid.."_EventHandler: "..EventData.id)
--self:I(self.lid.."_EventHandler: "..tostring(EventData.IniPlayerName))
if EventData.id == EVENTS.PlayerLeaveUnit or EventData.id == EVENTS.Ejection or EventData.id == EVENTS.Crash or EventData.id == EVENTS.PilotDead then
@@ -468,6 +468,10 @@ function CLIENTMENUMANAGER:_EventHandler(EventData)
if EventData.IniPlayerName and EventData.IniGroup then
if (not self.clientset:IsIncludeObject(_DATABASE:FindClient( EventData.IniUnitName ))) then
self:T(self.lid.."Client not in SET: "..EventData.IniPlayerName)
if not Retry then
-- try again in 2 secs
self:ScheduleOnce(2,CLIENTMENUMANAGER._EventHandler,self,EventData,true)
end
return self
end
--self:I(self.lid.."Join event for player: "..EventData.IniPlayerName)
@@ -524,7 +528,7 @@ function CLIENTMENUMANAGER:InitAutoPropagation()
self:HandleEvent(EVENTS.PilotDead, self._EventHandler)
self:HandleEvent(EVENTS.PlayerEnterAircraft, self._EventHandler)
self:HandleEvent(EVENTS.PlayerEnterUnit, self._EventHandler)
self:SetEventPriority(5)
self:SetEventPriority(6)
return self
end

View File

@@ -211,10 +211,9 @@ function DATABASE:AddStatic( DCSStaticName )
if not self.STATICS[DCSStaticName] then
self.STATICS[DCSStaticName] = STATIC:Register( DCSStaticName )
return self.STATICS[DCSStaticName]
end
return nil
return self.STATICS[DCSStaticName]
end
@@ -224,12 +223,11 @@ function DATABASE:DeleteStatic( DCSStaticName )
self.STATICS[DCSStaticName] = nil
end
--- Finds a STATIC based on the StaticName.
--- Finds a STATIC based on the Static Name.
-- @param #DATABASE self
-- @param #string StaticName
-- @param #string StaticName Name of the static object.
-- @return Wrapper.Static#STATIC The found STATIC.
function DATABASE:FindStatic( StaticName )
local StaticFound = self.STATICS[StaticName]
return StaticFound
end
@@ -241,9 +239,8 @@ end
function DATABASE:AddDynamicCargo( Name )
if not self.DYNAMICCARGO[Name] then
self.DYNAMICCARGO[Name] = DYNAMICCARGO:Register(Name)
return self.DYNAMICCARGO[Name]
end
return nil
return self.DYNAMICCARGO[Name]
end
--- Finds a DYNAMICCARGO based on the Dynamic Cargo Name.
@@ -875,6 +872,8 @@ end
-- @return Wrapper.Group#GROUP The found GROUP.
function DATABASE:FindGroup( GroupName )
if type(GroupName) ~= "string" or GroupName == "" then return end
local GroupFound = self.GROUPS[GroupName]
if GroupFound == nil and GroupName ~= nil and self.Templates.Groups[GroupName] == nil then
@@ -1582,12 +1581,29 @@ end
-- @param DCS#Airbase airbase Airbase.
-- @return #DATABASE self
function DATABASE:_RegisterAirbase(airbase)
local IsSyria = UTILS.GetDCSMap() == "Syria" and true or false
local countHSyria = 0
if airbase then
-- Get the airbase name.
local DCSAirbaseName = airbase:getName()
-- DCS 2.9.8.1107 added 143 helipads all named H with the same object ID ..
if IsSyria and DCSAirbaseName == "H" and countHSyria > 0 then
--[[
local p = airbase:getPosition().p
local mgrs = COORDINATE:New(p.x,p.z,p.y):ToStringMGRS()
self:I("Airbase on Syria map named H @ "..mgrs)
countHSyria = countHSyria + 1
if countHSyria > 1 then return self end
--]]
return self
elseif IsSyria and DCSAirbaseName == "H" and countHSyria == 0 then
countHSyria = countHSyria + 1
end
-- This gave the incorrect value to be inserted into the airdromeID for DCS 2.5.6. Is fixed now.
local airbaseID=airbase:getID()

View File

@@ -1329,6 +1329,7 @@ function EVENT:onEvent( Event )
end
Event.IniDCSGroupName = Event.IniUnit and Event.IniUnit.GroupName or ""
Event.IniGroupName=Event.IniDCSGroupName --At least set the group name because group might not exist any more
if Event.IniDCSGroup and Event.IniDCSGroup:isExist() then
Event.IniDCSGroupName = Event.IniDCSGroup:getName()
Event.IniGroup = GROUP:FindByName( Event.IniDCSGroupName )
@@ -1360,7 +1361,8 @@ function EVENT:onEvent( Event )
Event.IniDynamicCargoName = Event.IniUnitName
Event.IniPlayerName = string.match(Event.IniUnitName,"^(.+)|%d%d:%d%d|PKG%d+")
else
Event.IniUnit = CARGO:FindByName( Event.IniDCSUnitName )
--Event.IniUnit = CARGO:FindByName( Event.IniDCSUnitName )
Event.IniUnit = STATIC:FindByName( Event.IniDCSUnitName, false )
end
Event.IniCoalition = Event.IniDCSUnit:getCoalition()
Event.IniCategory = Event.IniDCSUnit:getDesc().category
@@ -1371,11 +1373,12 @@ function EVENT:onEvent( Event )
-- Scenery
---
Event.IniDCSUnit = Event.initiator
Event.IniDCSUnitName = Event.IniDCSUnit:getName()
Event.IniDCSUnitName = ( Event.IniDCSUnit and Event.IniDCSUnit.getName ) and Event.IniDCSUnit:getName() or "Scenery no name "..math.random(1,20000)
Event.IniUnitName = Event.IniDCSUnitName
Event.IniUnit = SCENERY:Register( Event.IniDCSUnitName, Event.initiator )
Event.IniCategory = Event.IniDCSUnit:getDesc().category
Event.IniTypeName = Event.initiator:isExist() and Event.IniDCSUnit:getTypeName() or "SCENERY"
Event.IniCategory = (Event.IniDCSUnit and Event.IniDCSUnit.getDesc ) and Event.IniDCSUnit:getDesc().category
Event.IniTypeName = (Event.initiator and Event.initiator.isExist
and Event.initiator:isExist() and Event.IniDCSUnit and Event.IniDCSUnit.getTypeName) and Event.IniDCSUnit:getTypeName() or "SCENERY"
elseif Event.IniObjectCategory == Object.Category.BASE then
---
@@ -1473,11 +1476,13 @@ function EVENT:onEvent( Event )
-- SCENERY
---
Event.TgtDCSUnit = Event.target
Event.TgtDCSUnitName = Event.TgtDCSUnit:getName()
Event.TgtUnitName = Event.TgtDCSUnitName
Event.TgtUnit = SCENERY:Register( Event.TgtDCSUnitName, Event.target )
Event.TgtCategory = Event.TgtDCSUnit:getDesc().category
Event.TgtTypeName = Event.TgtDCSUnit:getTypeName()
Event.TgtDCSUnitName = Event.TgtDCSUnit.getName and Event.TgtDCSUnit.getName() or nil
if Event.TgtDCSUnitName~=nil then
Event.TgtUnitName = Event.TgtDCSUnitName
Event.TgtUnit = SCENERY:Register( Event.TgtDCSUnitName, Event.target )
Event.TgtCategory = Event.TgtDCSUnit:getDesc().category
Event.TgtTypeName = Event.TgtDCSUnit:getTypeName()
end
end
end

View File

@@ -45,12 +45,6 @@
-- By efficiently utilizing the FSM class and derived classes, MOOSE allows mission designers to quickly build processes.
-- **Ready made FSM-based implementations classes** exist within the MOOSE framework that **can easily be re-used,
-- and tailored** by mission designers through **the implementation of Transition Handlers**.
-- Each of these FSM implementation classes start either with:
--
-- * an acronym **AI\_**, which indicates a FSM implementation directing **AI controlled** @{Wrapper.Group#GROUP} and/or @{Wrapper.Unit#UNIT}. These AI\_ classes derive the @{#FSM_CONTROLLABLE} class.
-- * an acronym **TASK\_**, which indicates a FSM implementation executing a @{Tasking.Task#TASK} executed by Groups of players. These TASK\_ classes derive the @{#FSM_TASK} class.
-- * an acronym **ACT\_**, which indicates an Sub-FSM implementation, directing **Humans actions** that need to be done in a @{Tasking.Task#TASK}, seated in a @{Wrapper.Client#CLIENT} (slot) or a @{Wrapper.Unit#UNIT} (CA join). These ACT\_ classes derive the @{#FSM_PROCESS} class.
--
-- Detailed explanations and API specifics are further below clarified and FSM derived class specifics are described in those class documentation sections.
--
-- ##__Disclaimer:__
@@ -61,7 +55,6 @@
--
-- The following derived classes are available in the MOOSE framework, that implement a specialized form of a FSM:
--
-- * @{#FSM_TASK}: Models Finite State Machines for @{Tasking.Task}s.
-- * @{#FSM_PROCESS}: Models Finite State Machines for @{Tasking.Task} actions, which control @{Wrapper.Client}s.
-- * @{#FSM_CONTROLLABLE}: Models Finite State Machines for @{Wrapper.Controllable}s, which are @{Wrapper.Group}s, @{Wrapper.Unit}s, @{Wrapper.Client}s.
-- * @{#FSM_SET}: Models Finite State Machines for @{Core.Set}s. Note that these FSMs control multiple objects!!! So State concerns here
@@ -78,7 +71,8 @@
-- @image Core_Finite_State_Machine.JPG
do -- FSM
---
-- @type FSM
-- @field #string ClassName Name of the class.
-- @field Core.Scheduler#SCHEDULER CallScheduler Call scheduler.
@@ -117,11 +111,6 @@ do -- FSM
-- By efficiently utilizing the FSM class and derived classes, MOOSE allows mission designers to quickly build processes.
-- **Ready made FSM-based implementations classes** exist within the MOOSE framework that **can easily be re-used,
-- and tailored** by mission designers through **the implementation of Transition Handlers**.
-- Each of these FSM implementation classes start either with:
--
-- * an acronym **AI\_**, which indicates an FSM implementation directing **AI controlled** @{Wrapper.Group#GROUP} and/or @{Wrapper.Unit#UNIT}. These AI\_ classes derive the @{#FSM_CONTROLLABLE} class.
-- * an acronym **TASK\_**, which indicates an FSM implementation executing a @{Tasking.Task#TASK} executed by Groups of players. These TASK\_ classes derive the @{#FSM_TASK} class.
-- * an acronym **ACT\_**, which indicates an Sub-FSM implementation, directing **Humans actions** that need to be done in a @{Tasking.Task#TASK}, seated in a @{Wrapper.Client#CLIENT} (slot) or a @{Wrapper.Unit#UNIT} (CA join). These ACT\_ classes derive the @{#FSM_PROCESS} class.
--
-- ![Transition Rules and Transition Handlers and Event Triggers](..\Presentations\FSM\Dia3.JPG)
--
@@ -948,7 +937,8 @@ do -- FSM
end
do -- FSM_CONTROLLABLE
---
-- @type FSM_CONTROLLABLE
-- @field Wrapper.Controllable#CONTROLLABLE Controllable
-- @extends Core.Fsm#FSM
@@ -1081,7 +1071,8 @@ do -- FSM_CONTROLLABLE
end
do -- FSM_PROCESS
---
-- @type FSM_PROCESS
-- @field Tasking.Task#TASK Task
-- @extends Core.Fsm#FSM_CONTROLLABLE

View File

@@ -108,26 +108,30 @@ function MARKEROPS_BASE:New(Tagname,Keywords,Casesensitive)
--- On after "MarkAdded" event. Triggered when a Marker is added to the F10 map.
-- @function [parent=#MARKEROPS_BASE] OnAfterMarkAdded
-- @param #MARKEROPS_BASE self
-- @param #string From The From state
-- @param #string Event The Event called
-- @param #string To The To state
-- @param #string Text The text on the marker
-- @param #table Keywords Table of matching keywords found in the Event text
-- @param #string From The From state.
-- @param #string Event The Event called.
-- @param #string To The To state.
-- @param #string Text The text on the marker.
-- @param #table Keywords Table of matching keywords found in the Event text.
-- @param Core.Point#COORDINATE Coord Coordinate of the marker.
-- @param #number MarkerID Id of this marker
-- @param #number CoalitionNumber Coalition of the marker creator
-- @param #number MarkerID Id of this marker.
-- @param #number CoalitionNumber Coalition of the marker creator.
-- @param #string PlayerName Name of the player creating/changing the mark. nil if it cannot be obtained.
-- @param Core.Event#EVENTDATA EventData the event data table.
--- On after "MarkChanged" event. Triggered when a Marker is changed on the F10 map.
-- @function [parent=#MARKEROPS_BASE] OnAfterMarkChanged
-- @param #MARKEROPS_BASE self
-- @param #string From The From state
-- @param #string Event The Event called
-- @param #string To The To state
-- @param #string Text The text on the marker
-- @param #table Keywords Table of matching keywords found in the Event text
-- @param #string From The From state.
-- @param #string Event The Event called.
-- @param #string To The To state.
-- @param #string Text The text on the marker.
-- @param #table Keywords Table of matching keywords found in the Event text.
-- @param Core.Point#COORDINATE Coord Coordinate of the marker.
-- @param #number MarkerID Id of this marker
-- @param #number CoalitionNumber Coalition of the marker creator
-- @param #number MarkerID Id of this marker.
-- @param #number CoalitionNumber Coalition of the marker creator.
-- @param #string PlayerName Name of the player creating/changing the mark. nil if it cannot be obtained.
-- @param Core.Event#EVENTDATA EventData the event data table
--- On after "MarkDeleted" event. Triggered when a Marker is deleted from the F10 map.
-- @function [parent=#MARKEROPS_BASE] OnAfterMarkDeleted
@@ -167,7 +171,7 @@ function MARKEROPS_BASE:OnEventMark(Event)
if Eventtext~=nil then
if self:_MatchTag(Eventtext) then
local matchtable = self:_MatchKeywords(Eventtext)
self:MarkAdded(Eventtext,matchtable,coord,Event.idx,coalition)
self:MarkAdded(Eventtext,matchtable,coord,Event.idx,coalition,Event.PlayerName,Event)
end
end
elseif Event.id==world.event.S_EVENT_MARK_CHANGE then
@@ -177,7 +181,7 @@ function MARKEROPS_BASE:OnEventMark(Event)
if Eventtext~=nil then
if self:_MatchTag(Eventtext) then
local matchtable = self:_MatchKeywords(Eventtext)
self:MarkChanged(Eventtext,matchtable,coord,Event.idx,coalition)
self:MarkChanged(Eventtext,matchtable,coord,Event.idx,coalition,Event.PlayerName,Event)
end
end
elseif Event.id==world.event.S_EVENT_MARK_REMOVED then

View File

@@ -105,6 +105,7 @@ function MENU_INDEX:PrepareCoalition( CoalitionSide )
self.Coalition[CoalitionSide] = self.Coalition[CoalitionSide] or {}
self.Coalition[CoalitionSide].Menus = self.Coalition[CoalitionSide].Menus or {}
end
---
-- @param Wrapper.Group#GROUP Group
function MENU_INDEX:PrepareGroup( Group )
@@ -118,9 +119,11 @@ end
function MENU_INDEX:HasMissionMenu( Path )
return self.MenuMission.Menus[Path]
end
function MENU_INDEX:SetMissionMenu( Path, Menu )
self.MenuMission.Menus[Path] = Menu
end
function MENU_INDEX:ClearMissionMenu( Path )
self.MenuMission.Menus[Path] = nil
end
@@ -128,9 +131,11 @@ end
function MENU_INDEX:HasCoalitionMenu( Coalition, Path )
return self.Coalition[Coalition].Menus[Path]
end
function MENU_INDEX:SetCoalitionMenu( Coalition, Path, Menu )
self.Coalition[Coalition].Menus[Path] = Menu
end
function MENU_INDEX:ClearCoalitionMenu( Coalition, Path )
self.Coalition[Coalition].Menus[Path] = nil
end
@@ -138,19 +143,24 @@ end
function MENU_INDEX:HasGroupMenu( Group, Path )
if Group and Group:IsAlive() then
local MenuGroupName = Group:GetName()
return self.Group[MenuGroupName].Menus[Path]
if self.Group[MenuGroupName] and self.Group[MenuGroupName].Menus and self.Group[MenuGroupName].Menus[Path] then
return self.Group[MenuGroupName].Menus[Path]
end
end
return nil
end
function MENU_INDEX:SetGroupMenu( Group, Path, Menu )
local MenuGroupName = Group:GetName()
Group:F({MenuGroupName=MenuGroupName,Path=Path})
--Group:F({MenuGroupName=MenuGroupName,Path=Path})
self.Group[MenuGroupName].Menus[Path] = Menu
end
function MENU_INDEX:ClearGroupMenu( Group, Path )
local MenuGroupName = Group:GetName()
self.Group[MenuGroupName].Menus[Path] = nil
end
function MENU_INDEX:Refresh( Group )
for MenuID, Menu in pairs( self.MenuMission.Menus ) do
Menu:Refresh()

View File

@@ -75,35 +75,37 @@ MESSAGE.Type = {
--- Creates a new MESSAGE object. Note that these MESSAGE objects are not yet displayed on the display panel. You must use the functions @{#MESSAGE.ToClient} or @{#MESSAGE.ToCoalition} or @{#MESSAGE.ToAll} to send these Messages to the respective recipients.
-- @param self
-- @param #string MessageText is the text of the Message.
-- @param #number MessageDuration is a number in seconds of how long the MESSAGE should be shown on the display panel.
-- @param #string MessageCategory (optional) is a string expressing the "category" of the Message. The category will be shown as the first text in the message followed by a ": ".
-- @param #string Text is the text of the Message.
-- @param #number Duration Duration in seconds how long the message text is shown.
-- @param #string Category (Optional) String expressing the "category" of the Message. The category will be shown as the first text in the message followed by a ": ".
-- @param #boolean ClearScreen (optional) Clear all previous messages if true.
-- @return #MESSAGE
-- @return #MESSAGE self
-- @usage
--
-- -- Create a series of new Messages.
-- -- MessageAll is meant to be sent to all players, for 25 seconds, and is classified as "Score".
-- -- MessageRED is meant to be sent to the RED players only, for 10 seconds, and is classified as "End of Mission", with ID "Win".
-- -- MessageClient1 is meant to be sent to a Client, for 25 seconds, and is classified as "Score", with ID "Score".
-- -- MessageClient1 is meant to be sent to a Client, for 25 seconds, and is classified as "Score", with ID "Score".
-- -- Create a series of new Messages.
-- -- MessageAll is meant to be sent to all players, for 25 seconds, and is classified as "Score".
-- -- MessageRED is meant to be sent to the RED players only, for 10 seconds, and is classified as "End of Mission", with ID "Win".
-- -- MessageClient1 is meant to be sent to a Client, for 25 seconds, and is classified as "Score", with ID "Score".
-- -- MessageClient1 is meant to be sent to a Client, for 25 seconds, and is classified as "Score", with ID "Score".
-- MessageAll = MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", 25, "End of Mission" )
-- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", 25, "Penalty" )
-- MessageClient1 = MESSAGE:New( "Congratulations, you've just hit a target", 25, "Score" )
-- MessageClient2 = MESSAGE:New( "Congratulations, you've just killed a target", 25, "Score")
--
function MESSAGE:New( MessageText, MessageDuration, MessageCategory, ClearScreen )
function MESSAGE:New( Text, Duration, Category, ClearScreen )
local self = BASE:Inherit( self, BASE:New() )
self:F( { MessageText, MessageDuration, MessageCategory } )
self:F( { Text, Duration, Category } )
self.MessageType = nil
-- When no MessageCategory is given, we don't show it as a title...
if MessageCategory and MessageCategory ~= "" then
if MessageCategory:sub( -1 ) ~= "\n" then
self.MessageCategory = MessageCategory .. ": "
if Category and Category ~= "" then
if Category:sub( -1 ) ~= "\n" then
self.MessageCategory = Category .. ": "
else
self.MessageCategory = MessageCategory:sub( 1, -2 ) .. ":\n"
self.MessageCategory = Category:sub( 1, -2 ) .. ":\n"
end
else
self.MessageCategory = ""
@@ -114,9 +116,9 @@ function MESSAGE:New( MessageText, MessageDuration, MessageCategory, ClearScreen
self.ClearScreen = ClearScreen
end
self.MessageDuration = MessageDuration or 5
self.MessageDuration = Duration or 5
self.MessageTime = timer.getTime()
self.MessageText = MessageText:gsub( "^\n", "", 1 ):gsub( "\n$", "", 1 )
self.MessageText = Text:gsub( "^\n", "", 1 ):gsub( "\n$", "", 1 )
self.MessageSent = false
self.MessageGroup = false
@@ -204,7 +206,7 @@ end
function MESSAGE:ToGroup( Group, Settings )
self:F( Group.GroupName )
if Group then
if Group and Group:IsAlive() then
if self.MessageType then
local Settings = Settings or (Group and _DATABASE:GetPlayerSettings( Group:GetPlayerName() )) or _SETTINGS -- Core.Settings#SETTINGS
@@ -229,7 +231,7 @@ end
function MESSAGE:ToUnit( Unit, Settings )
self:F( Unit.IdentifiableName )
if Unit then
if Unit and Unit:IsAlive() then
if self.MessageType then
local Settings = Settings or ( Unit and _DATABASE:GetPlayerSettings( Unit:GetPlayerName() ) ) or _SETTINGS -- Core.Settings#SETTINGS
@@ -462,6 +464,7 @@ _MESSAGESRS = {}
-- @param #number Volume (optional) Volume, can be between 0.0 and 1.0 (loudest).
-- @param #string Label (optional) Label, defaults to "MESSAGE" or the Message Category set.
-- @param Core.Point#COORDINATE Coordinate (optional) Coordinate this messages originates from.
-- @param #string Backend (optional) Backend to be used, can be MSRS.Backend.SRSEXE or MSRS.Backend.GRPC
-- @usage
-- -- Mind the dot here, not using the colon this time around!
-- -- Needed once only
@@ -469,7 +472,7 @@ _MESSAGESRS = {}
-- -- later on in your code
-- MESSAGE:New("Test message!",15,"SPAWN"):ToSRS()
--
function MESSAGE.SetMSRS(PathToSRS,Port,PathToCredentials,Frequency,Modulation,Gender,Culture,Voice,Coalition,Volume,Label,Coordinate)
function MESSAGE.SetMSRS(PathToSRS,Port,PathToCredentials,Frequency,Modulation,Gender,Culture,Voice,Coalition,Volume,Label,Coordinate,Backend)
_MESSAGESRS.PathToSRS = PathToSRS or MSRS.path or "C:\\Program Files\\DCS-SimpleRadio-Standalone"
@@ -487,6 +490,10 @@ function MESSAGE.SetMSRS(PathToSRS,Port,PathToCredentials,Frequency,Modulation,G
_MESSAGESRS.MSRS:SetCoordinate(Coordinate)
end
if Backend then
_MESSAGESRS.MSRS:SetBackend(Backend)
end
_MESSAGESRS.Culture = Culture or MSRS.culture or "en-GB"
_MESSAGESRS.MSRS:SetCulture(Culture)

View File

@@ -25,7 +25,7 @@
do -- COORDINATE
---
--- Coordinate class
-- @type COORDINATE
-- @field #string ClassName Name of the class
-- @field #number x Component of the 3D vector.
@@ -110,7 +110,7 @@ do -- COORDINATE
--
-- ## 4.4) Get the North correction of the current location.
--
-- * @{#COORDINATE.GetNorthCorrection}(): Obtains the north correction at the current 3D point.
-- * @{#COORDINATE.GetNorthCorrectionRadians}(): Obtains the north correction at the current 3D point.
--
-- ## 4.5) Point Randomization
--
@@ -968,8 +968,13 @@ do -- COORDINATE
-- @return DCS#Distance Distance The distance in meters.
function COORDINATE:Get2DDistance(TargetCoordinate)
if not TargetCoordinate then return 1000000 end
local a={x=TargetCoordinate.x-self.x, y=0, z=TargetCoordinate.z-self.z}
local norm=UTILS.VecNorm(a)
--local a={x=TargetCoordinate.x-self.x, y=0, z=TargetCoordinate.z-self.z}
local a = self:GetVec2()
if not TargetCoordinate.ClassName then
TargetCoordinate=COORDINATE:NewFromVec3(TargetCoordinate)
end
local b = TargetCoordinate:GetVec2()
local norm=UTILS.VecDist2D(a,b)
return norm
end
@@ -1152,6 +1157,162 @@ do -- COORDINATE
return vec3
end
--- Return the x coordinate of the COORDINATE.
-- @param #COORDINATE self
-- @return #number The x coordinate.
function COORDINATE:GetX()
return self.x
end
--- Return the y coordinate of the COORDINATE.
-- @param #COORDINATE self
-- @return #number The y coordinate.
function COORDINATE:GetY()
if self:IsInstanceOf("POINT_VEC2") then
return self.z
end
return self.y
end
--- Return the z coordinate of the COORDINATE.
-- @param #COORDINATE self
-- @return #number The z coordinate.
function COORDINATE:GetZ()
return self.z
end
--- Set the x coordinate of the COORDINATE.
-- @param #COORDINATE self
-- @param #number x The x coordinate.
-- @return #COORDINATE
function COORDINATE:SetX( x )
self.x = x
return self
end
--- Set the y coordinate of the COORDINATE.
-- @param #COORDINATE self
-- @param #number y The y coordinate.
-- @return #COORDINATE
function COORDINATE:SetY( y )
if self:IsInstanceOf("POINT_VEC2") then
self.z = y
else
self.y = y
end
return self
end
--- Set the z coordinate of the COORDINATE.
-- @param #COORDINATE self
-- @param #number z The z coordinate.
-- @return #COORDINATE
function COORDINATE:SetZ( z )
self.z = z
return self
end
--- Add to the x coordinate of the COORDINATE.
-- @param #COORDINATE self
-- @param #number x The x coordinate value to add to the current x coordinate.
-- @return #COORDINATE
function COORDINATE:AddX( x )
self.x = self.x + x
return self
end
--- Return Return the Lat(itude) coordinate of the COORDINATE (ie: (parent)COORDINATE.x).
-- @param #COORDINATE self
-- @return #number The x coordinate.
function COORDINATE:GetLat()
return self.x
end
--- Set the Lat(itude) coordinate of the COORDINATE (ie: COORDINATE.x).
-- @param #COORDINATE self
-- @param #number x The x coordinate.
-- @return #COORDINATE
function COORDINATE:SetLat( x )
self.x = x
return self
end
--- Return the Lon(gitude) coordinate of the COORDINATE (ie: (parent)COORDINATE.z).
-- @param #COORDINATE self
-- @return #number The y coordinate.
function COORDINATE:GetLon()
return self.z
end
--- Set the Lon(gitude) coordinate of the COORDINATE (ie: COORDINATE.z).
-- @param #COORDINATE self
-- @param #number y The y coordinate.
-- @return #COORDINATE
function COORDINATE:SetLon( z )
self.z = z
return self
end
--- Return the altitude (height) of the land at the COORDINATE.
-- @param #COORDINATE self
-- @return #number The land altitude.
function COORDINATE:GetAlt()
return self.y ~= 0 or land.getHeight( { x = self.x, y = self.z } )
end
--- Set the altitude of the COORDINATE.
-- @param #COORDINATE self
-- @param #number Altitude The land altitude. If nothing (nil) is given, then the current land altitude is set.
-- @return #COORDINATE
function COORDINATE:SetAlt( Altitude )
self.y = Altitude or land.getHeight( { x = self.x, y = self.z } )
return self
end
--- Add to the current land height an altitude.
-- @param #COORDINATE self
-- @param #number Altitude The Altitude to add. If nothing (nil) is given, then the current land altitude is set.
-- @return #COORDINATE
function COORDINATE:AddAlt( Altitude )
self.y = land.getHeight( { x = self.x, y = self.z } ) + Altitude or 0
return self
end
--- Return a random COORDINATE within an Outer Radius and optionally NOT within an Inner Radius of the COORDINATE.
-- @param #COORDINATE self
-- @param DCS#Distance OuterRadius
-- @param DCS#Distance InnerRadius
-- @return #COORDINATE
function COORDINATE:GetRandomPointVec2InRadius( OuterRadius, InnerRadius )
self:F2( { OuterRadius, InnerRadius } )
return COORDINATE:NewFromVec2( self:GetRandomVec2InRadius( OuterRadius, InnerRadius ) )
end
--- Add to the y coordinate of the COORDINATE.
-- @param #COORDINATE self
-- @param #number y The y coordinate value to add to the current y coordinate.
-- @return #COORDINATE
function COORDINATE:AddY( y )
if self:IsInstanceOf("POINT_VEC2") then
return self:AddZ(y)
else
self.y = self.y + y
end
return self
end
--- Add to the z coordinate of the COORDINATE.
-- @param #COORDINATE self
-- @param #number z The z coordinate value to add to the current z coordinate.
-- @return #COORDINATE
function COORDINATE:AddZ( z )
self.z = self.z +z
return self
end
--- Returns a text documenting the wind direction (from) and strength according the measurement system @{Core.Settings}.
-- The text will reflect the wind like this:
@@ -1216,7 +1377,7 @@ do -- COORDINATE
local s = string.format( '%03d°', AngleDegrees )
if MagVar then
local variation = UTILS.GetMagneticDeclination() or 0
local variation = self:GetMagneticDeclination() or 0
local AngleMagnetic = AngleDegrees - variation
if AngleMagnetic < 0 then AngleMagnetic = 360-AngleMagnetic end
@@ -1329,13 +1490,16 @@ do -- COORDINATE
-- @param Core.Settings#SETTINGS Settings
-- @param #string Language (Optional) Language "en" or "ru"
-- @param #boolean MagVar If true, also state angle in magnetic
-- @param #number Precision Rounding precision, defaults to 0
-- @return #string The BR Text
function COORDINATE:GetBRText( AngleRadians, Distance, Settings, Language, MagVar )
function COORDINATE:GetBRText( AngleRadians, Distance, Settings, Language, MagVar, Precision )
local Settings = Settings or _SETTINGS -- Core.Settings#SETTINGS
Precision = Precision or 0
local BearingText = self:GetBearingText( AngleRadians, 0, Settings, MagVar )
local DistanceText = self:GetDistanceText( Distance, Settings, Language, 0 )
local DistanceText = self:GetDistanceText( Distance, Settings, Language, Precision )
local BRText = BearingText .. DistanceText
@@ -1954,59 +2118,123 @@ do -- COORDINATE
end
--- Smokes the point in a color.
--- Create colored smoke the point. The smoke we last up to 5 min (DCS limitation) but you can optionally specify a shorter duration or stop it manually.
-- @param #COORDINATE self
-- @param Utilities.Utils#SMOKECOLOR SmokeColor
function COORDINATE:Smoke( SmokeColor )
self:F2( { SmokeColor } )
trigger.action.smoke( self:GetVec3(), SmokeColor )
-- @param #number SmokeColor Color of smoke, e.g. `SMOKECOLOR.Green` for green smoke.
-- @param #number Duration (Optional) Duration of the smoke in seconds. DCS stopps the smoke automatically after 5 min.
-- @param #number Delay (Optional) Delay before the smoke is started in seconds.
-- @param #string Name (Optional) Name if you want to stop the smoke early (normal duration: 5mins)
-- @return #COORDINATE self
function COORDINATE:Smoke( SmokeColor, Duration, Delay, Name)
self:F2( { SmokeColor, Name, Duration, Delay } )
SmokeColor=SmokeColor or SMOKECOLOR.Green
if Delay and Delay>0 then
self:ScheduleOnce(Delay, COORDINATE.Smoke, self, SmokeColor, Duration, 0, Name)
else
-- Create a name which is used to stop the smoke manually
self.firename = Name or "Smoke-"..math.random(1,100000)
-- Create smoke
trigger.action.smoke( self:GetVec3(), SmokeColor, self.firename )
-- Stop smoke
if Duration and Duration>0 then
self:ScheduleOnce(Duration, COORDINATE.StopSmoke, self, self.firename )
end
end
return self
end
--- Stops smoking the point in a color.
-- @param #COORDINATE self
-- @param #string name (Optional) Name if you want to stop the smoke early (normal duration: 5mins)
function COORDINATE:StopSmoke( name )
self:StopBigSmokeAndFire( name )
end
--- Smoke the COORDINATE Green.
-- @param #COORDINATE self
function COORDINATE:SmokeGreen()
self:F2()
self:Smoke( SMOKECOLOR.Green )
-- @param #number Duration (Optional) Duration of the smoke in seconds. DCS stopps the smoke automatically after 5 min.
-- @param #number Delay (Optional) Delay before the smoke is started in seconds.
-- @return #COORDINATE self
function COORDINATE:SmokeGreen(Duration, Delay)
self:Smoke( SMOKECOLOR.Green, Duration, Delay )
return self
end
--- Smoke the COORDINATE Red.
-- @param #COORDINATE self
function COORDINATE:SmokeRed()
self:F2()
self:Smoke( SMOKECOLOR.Red )
-- @param #number Duration (Optional) Duration of the smoke in seconds. DCS stopps the smoke automatically after 5 min.
-- @param #number Delay (Optional) Delay before the smoke is started in seconds.
-- @return #COORDINATE self
function COORDINATE:SmokeRed(Duration, Delay)
self:Smoke( SMOKECOLOR.Red, Duration, Delay )
return self
end
--- Smoke the COORDINATE White.
-- @param #COORDINATE self
function COORDINATE:SmokeWhite()
self:F2()
self:Smoke( SMOKECOLOR.White )
-- @param #number Duration (Optional) Duration of the smoke in seconds. DCS stopps the smoke automatically after 5 min.
-- @param #number Delay (Optional) Delay before the smoke is started in seconds.
-- @return #COORDINATE self
function COORDINATE:SmokeWhite(Duration, Delay)
self:Smoke( SMOKECOLOR.White, Duration, Delay )
return self
end
--- Smoke the COORDINATE Orange.
-- @param #COORDINATE self
function COORDINATE:SmokeOrange()
self:F2()
self:Smoke( SMOKECOLOR.Orange )
-- @param #number Duration (Optional) Duration of the smoke in seconds. DCS stopps the smoke automatically after 5 min.
-- @param #number Delay (Optional) Delay before the smoke is started in seconds.
-- @return #COORDINATE self
function COORDINATE:SmokeOrange(Duration, Delay)
self:Smoke( SMOKECOLOR.Orange, Duration, Delay )
return self
end
--- Smoke the COORDINATE Blue.
-- @param #COORDINATE self
function COORDINATE:SmokeBlue()
self:F2()
self:Smoke( SMOKECOLOR.Blue )
-- @param #number Duration (Optional) Duration of the smoke in seconds. DCS stopps the smoke automatically after 5 min.
-- @param #number Delay (Optional) Delay before the smoke is started in seconds.
-- @return #COORDINATE self
function COORDINATE:SmokeBlue(Duration, Delay)
self:Smoke( SMOKECOLOR.Blue, Duration, Delay )
return self
end
--- Big smoke and fire at the coordinate.
-- @param #COORDINATE self
-- @param Utilities.Utils#BIGSMOKEPRESET preset Smoke preset (1=small smoke and fire, 2=medium smoke and fire, 3=large smoke and fire, 4=huge smoke and fire, 5=small smoke, 6=medium smoke, 7=large smoke, 8=huge smoke).
-- @param #number density (Optional) Smoke density. Number in [0,...,1]. Default 0.5.
-- @param #string name (Optional) Name of the fire to stop it later again if not using the same COORDINATE object. Defaults to "Fire-" plus a random 5-digit-number.
function COORDINATE:BigSmokeAndFire( preset, density, name )
self:F2( { preset=preset, density=density } )
density=density or 0.5
self.firename = name or "Fire-"..math.random(1,10000)
trigger.action.effectSmokeBig( self:GetVec3(), preset, density, self.firename )
-- @param #number Preset Smoke preset (1=small smoke and fire, 2=medium smoke and fire, 3=large smoke and fire, 4=huge smoke and fire, 5=small smoke, 6=medium smoke, 7=large smoke, 8=huge smoke).
-- @param #number Density (Optional) Smoke density. Number in [0,...,1]. Default 0.5.
-- @param #number Duration (Optional) Duration of the smoke and fire in seconds.
-- @param #number Delay (Optional) Delay before the smoke and fire is started in seconds.
-- @param #string Name (Optional) Name of the fire to stop it later again if not using the same COORDINATE object. Defaults to "Fire-" plus a random 5-digit-number.
-- @return #COORDINATE self
function COORDINATE:BigSmokeAndFire( Preset, Density, Duration, Delay, Name )
self:F2( { preset=Preset, density=Density } )
Preset=Preset or BIGSMOKEPRESET.SmallSmokeAndFire
Density=Density or 0.5
if Delay and Delay>0 then
self:ScheduleOnce(Delay, COORDINATE.BigSmokeAndFire, self, Preset, Density, Duration, 0, Name)
else
self.firename = Name or "Fire-"..math.random(1,10000)
trigger.action.effectSmokeBig( self:GetVec3(), Preset, Density, self.firename )
-- Stop smoke
if Duration and Duration>0 then
self:ScheduleOnce(Duration, COORDINATE.StopBigSmokeAndFire, self, self.firename )
end
end
return self
end
--- Stop big smoke and fire at the coordinate.
@@ -2019,82 +2247,98 @@ do -- COORDINATE
--- Small smoke and fire at the coordinate.
-- @param #COORDINATE self
-- @param #number density (Optional) Smoke density. Number between 0 and 1. Default 0.5.
-- @param #string name (Optional) Name of the fire to stop it later again if not using the same COORDINATE object. Defaults to "Fire-" plus a random 5-digit-number.
function COORDINATE:BigSmokeAndFireSmall( density, name )
self:F2( { density=density } )
density=density or 0.5
self:BigSmokeAndFire(BIGSMOKEPRESET.SmallSmokeAndFire, density, name)
-- @param #number Density (Optional) Smoke density. Number between 0 and 1. Default 0.5.
-- @param #number Duration (Optional) Duration of the smoke and fire in seconds.
-- @param #number Delay (Optional) Delay before the smoke and fire is started in seconds.
-- @param #string Name (Optional) Name of the fire to stop it later again if not using the same COORDINATE object. Defaults to "Fire-" plus a random 5-digit-number.
-- @return #COORDINATE self
function COORDINATE:BigSmokeAndFireSmall( Density, Duration, Delay, Name )
self:BigSmokeAndFire(BIGSMOKEPRESET.SmallSmokeAndFire, Density, Duration, Delay, Name)
return self
end
--- Medium smoke and fire at the coordinate.
-- @param #COORDINATE self
-- @param #number density (Optional) Smoke density. Number between 0 and 1. Default 0.5.
-- @param #number Duration (Optional) Duration of the smoke and fire in seconds.
-- @param #number Delay (Optional) Delay before the smoke and fire is started in seconds.
-- @param #string name (Optional) Name of the fire to stop it later again if not using the same COORDINATE object. Defaults to "Fire-" plus a random 5-digit-number.
function COORDINATE:BigSmokeAndFireMedium( density, name )
self:F2( { density=density } )
density=density or 0.5
self:BigSmokeAndFire(BIGSMOKEPRESET.MediumSmokeAndFire, density, name)
-- @return #COORDINATE self
function COORDINATE:BigSmokeAndFireMedium( Density, Duration, Delay, Name )
self:BigSmokeAndFire(BIGSMOKEPRESET.MediumSmokeAndFire, Density, Duration, Delay, Name)
return self
end
--- Large smoke and fire at the coordinate.
-- @param #COORDINATE self
-- @param #number density (Optional) Smoke density. Number between 0 and 1. Default 0.5.
-- @param #number Duration (Optional) Duration of the smoke and fire in seconds.
-- @param #number Delay (Optional) Delay before the smoke and fire is started in seconds.
-- @param #string name (Optional) Name of the fire to stop it later again if not using the same COORDINATE object. Defaults to "Fire-" plus a random 5-digit-number.
function COORDINATE:BigSmokeAndFireLarge( density, name )
self:F2( { density=density } )
density=density or 0.5
self:BigSmokeAndFire(BIGSMOKEPRESET.LargeSmokeAndFire, density, name)
-- @return #COORDINATE self
function COORDINATE:BigSmokeAndFireLarge( Density, Duration, Delay, Name )
self:BigSmokeAndFire(BIGSMOKEPRESET.LargeSmokeAndFire, Density, Duration, Delay, Name)
return self
end
--- Huge smoke and fire at the coordinate.
-- @param #COORDINATE self
-- @param #number density (Optional) Smoke density. Number between 0 and 1. Default 0.5.
-- @param #number Duration (Optional) Duration of the smoke and fire in seconds.
-- @param #number Delay (Optional) Delay before the smoke and fire is started in seconds.
-- @param #string name (Optional) Name of the fire to stop it later again if not using the same COORDINATE object. Defaults to "Fire-" plus a random 5-digit-number.
function COORDINATE:BigSmokeAndFireHuge( density, name )
self:F2( { density=density } )
density=density or 0.5
self:BigSmokeAndFire(BIGSMOKEPRESET.HugeSmokeAndFire, density, name)
-- @return #COORDINATE self
function COORDINATE:BigSmokeAndFireHuge( Density, Duration, Delay, Name )
self:BigSmokeAndFire(BIGSMOKEPRESET.HugeSmokeAndFire, Density, Duration, Delay, Name)
return self
end
--- Small smoke at the coordinate.
-- @param #COORDINATE self
-- @param #number density (Optional) Smoke density. Number between 0 and 1. Default 0.5.
-- @param #number Duration (Optional) Duration of the smoke and fire in seconds.
-- @param #number Delay (Optional) Delay before the smoke and fire is started in seconds.
-- @param #string name (Optional) Name of the fire to stop it later again if not using the same COORDINATE object. Defaults to "Fire-" plus a random 5-digit-number.
function COORDINATE:BigSmokeSmall( density, name )
self:F2( { density=density } )
density=density or 0.5
self:BigSmokeAndFire(BIGSMOKEPRESET.SmallSmoke, density, name)
-- @return #COORDINATE self
function COORDINATE:BigSmokeSmall( Density, Duration, Delay, Name )
self:BigSmokeAndFire(BIGSMOKEPRESET.SmallSmoke, Density, Duration, Delay, Name)
return self
end
--- Medium smoke at the coordinate.
-- @param #COORDINATE self
-- @param number density (Optional) Smoke density. Number between 0 and 1. Default 0.5.
-- @param #number Duration (Optional) Duration of the smoke and fire in seconds.
-- @param #number Delay (Optional) Delay before the smoke and fire is started in seconds.
-- @param #string name (Optional) Name of the fire to stop it later again if not using the same COORDINATE object. Defaults to "Fire-" plus a random 5-digit-number.
function COORDINATE:BigSmokeMedium( density, name )
self:F2( { density=density } )
density=density or 0.5
self:BigSmokeAndFire(BIGSMOKEPRESET.MediumSmoke, density, name)
-- @return #COORDINATE self
function COORDINATE:BigSmokeMedium( Density, Duration, Delay, Name )
self:BigSmokeAndFire(BIGSMOKEPRESET.MediumSmoke, Density, Duration, Delay, Name)
return self
end
--- Large smoke at the coordinate.
-- @param #COORDINATE self
-- @param #number density (Optional) Smoke density. Number between 0 and 1. Default 0.5.
-- @param #number Duration (Optional) Duration of the smoke and fire in seconds.
-- @param #number Delay (Optional) Delay before the smoke and fire is started in seconds.
-- @param #string name (Optional) Name of the fire to stop it later again if not using the same COORDINATE object. Defaults to "Fire-" plus a random 5-digit-number.
function COORDINATE:BigSmokeLarge( density, name )
self:F2( { density=density } )
density=density or 0.5
self:BigSmokeAndFire(BIGSMOKEPRESET.LargeSmoke, density,name)
-- @return #COORDINATE self
function COORDINATE:BigSmokeLarge( Density, Duration, Delay, Name )
self:BigSmokeAndFire(BIGSMOKEPRESET.LargeSmoke, Density, Duration, Delay, Name)
return self
end
--- Huge smoke at the coordinate.
-- @param #COORDINATE self
-- @param #number density (Optional) Smoke density. Number between 0 and 1. Default 0.5.
-- @param #number Duration (Optional) Duration of the smoke and fire in seconds.
-- @param #number Delay (Optional) Delay before the smoke and fire is started in seconds.
-- @param #string name (Optional) Name of the fire to stop it later again if not using the same COORDINATE object. Defaults to "Fire-" plus a random 5-digit-number.
function COORDINATE:BigSmokeHuge( density, name )
self:F2( { density=density } )
density=density or 0.5
self:BigSmokeAndFire(BIGSMOKEPRESET.HugeSmoke, density,name)
-- @return #COORDINATE self
function COORDINATE:BigSmokeHuge( Density, Duration, Delay, Name )
self:BigSmokeAndFire(BIGSMOKEPRESET.HugeSmoke, Density, Duration, Delay, Name)
return self
end
--- Flares the point in a color.
@@ -2410,7 +2654,7 @@ do -- COORDINATE
for i,coord in ipairs(Coordinates) do
vecs[i+1]=coord:GetVec3()
end
if #vecs<3 then
self:E("ERROR: A free form polygon needs at least three points!")
elseif #vecs==3 then
@@ -2748,8 +2992,10 @@ do -- COORDINATE
local sunrise=UTILS.GetSunRiseAndSet(DayOfYear, Latitude, Longitude, true, Tdiff)
local sunset=UTILS.GetSunRiseAndSet(DayOfYear, Latitude, Longitude, false, Tdiff)
if sunrise == "N/R" then return false end
if sunrise == "N/S" then return true end
if type(sunrise) == "string" or type(sunset) == "string" then
if sunrise == "N/R" then return false end
if sunset == "N/S" then return true end
end
local time=UTILS.ClockToSeconds(clock)
@@ -2767,6 +3013,11 @@ do -- COORDINATE
-- Todays sun set in sec.
local sunset=self:GetSunset(true)
if type(sunrise) == "string" or type(sunset) == "string" then
if sunrise == "N/R" then return false end
if sunset == "N/S" then return true end
end
-- Seconds passed since midnight.
local time=UTILS.SecondsOfToday()
@@ -2900,12 +3151,13 @@ do -- COORDINATE
-- @param #COORDINATE FromCoordinate The coordinate to measure the distance and the bearing from.
-- @param Core.Settings#SETTINGS Settings (optional) The settings. Can be nil, and in this case the default settings are used. If you want to specify your own settings, use the _SETTINGS object.
-- @param #boolean MagVar If true, also get angle in MagVar for BR/BRA
-- @param #number Precision Rounding precision, currently full km as default (=0)
-- @return #string The BR text.
function COORDINATE:ToStringBR( FromCoordinate, Settings, MagVar )
function COORDINATE:ToStringBR( FromCoordinate, Settings, MagVar, Precision )
local DirectionVec3 = FromCoordinate:GetDirectionVec3( self )
local AngleRadians = self:GetAngleRadians( DirectionVec3 )
local Distance = self:Get2DDistance( FromCoordinate )
return "BR, " .. self:GetBRText( AngleRadians, Distance, Settings, nil, MagVar )
return "BR, " .. self:GetBRText( AngleRadians, Distance, Settings, nil, MagVar, Precision )
end
--- Return a BRA string from a COORDINATE to the COORDINATE.
@@ -2941,6 +3193,8 @@ do -- COORDINATE
local AngleRadians = self:GetAngleRadians( DirectionVec3 )
local bearing = UTILS.Round( UTILS.ToDegree( AngleRadians ),0 )
local magnetic = self:GetMagneticDeclination() or 0
bearing = bearing - magnetic
local rangeMetres = self:Get2DDistance(currentCoord)
local rangeNM = UTILS.Round( UTILS.MetersToNM(rangeMetres), 0)
@@ -3313,16 +3567,16 @@ do -- COORDINATE
-- @param #COORDINATE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The controllable to retrieve the settings from, otherwise the default settings will be chosen.
-- @param Core.Settings#SETTINGS Settings (optional) The settings. Can be nil, and in this case the default settings are used. If you want to specify your own settings, use the _SETTINGS object.
-- @param Tasking.Task#TASK Task The task for which coordinates need to be calculated.
-- @return #string The coordinate Text in the configured coordinate system.
function COORDINATE:ToString( Controllable, Settings, Task )
function COORDINATE:ToString( Controllable, Settings )
-- self:E( { Controllable = Controllable and Controllable:GetName() } )
local Settings = Settings or ( Controllable and _DATABASE:GetPlayerSettings( Controllable:GetPlayerName() ) ) or _SETTINGS
local ModeA2A = nil
--[[
if Task then
if Task:IsInstanceOf( TASK_A2A ) then
ModeA2A = true
@@ -3339,7 +3593,7 @@ do -- COORDINATE
end
end
end
--]]
if ModeA2A == nil then
local IsAir = Controllable and ( Controllable:IsAirPlane() or Controllable:IsHelicopter() ) or false
@@ -3454,9 +3708,18 @@ do -- COORDINATE
return flat, elev
end
--- Return a random COORDINATE within an Outer Radius and optionally NOT within an Inner Radius of the COORDINATE.
-- @param #COORDINATE self
-- @param DCS#Distance OuterRadius
-- @param DCS#Distance InnerRadius
-- @return #COORDINATE
function COORDINATE:GetRandomPointVec3InRadius( OuterRadius, InnerRadius )
return COORDINATE:NewFromVec3( self:GetRandomVec3InRadius( OuterRadius, InnerRadius ) )
end
end
do -- POINT_VEC3
do
--- The POINT_VEC3 class
-- @type POINT_VEC3
@@ -3473,6 +3736,8 @@ do -- POINT_VEC3
--- Defines a 3D point in the simulator and with its methods, you can use or manipulate the point in 3D space.
--
-- **DEPRECATED - PLEASE USE COORDINATE!**
--
-- **Important Note:** Most of the functions in this section were taken from MIST, and reworked to OO concepts.
-- In order to keep the credibility of the the author,
-- I want to emphasize that the formulas embedded in the MIST framework were created by Grimes or previous authors,
@@ -3560,130 +3825,19 @@ do -- POINT_VEC3
return self
end
--- Create a new POINT_VEC3 object from Vec2 coordinates.
-- @param #POINT_VEC3 self
-- @param DCS#Vec2 Vec2 The Vec2 point.
-- @param DCS#Distance LandHeightAdd (optional) Add a landheight.
-- @return Core.Point#POINT_VEC3 self
function POINT_VEC3:NewFromVec2( Vec2, LandHeightAdd )
local self = BASE:Inherit( self, COORDINATE:NewFromVec2( Vec2, LandHeightAdd ) ) -- Core.Point#POINT_VEC3
self:F2( self )
return self
end
--- Create a new POINT_VEC3 object from Vec3 coordinates.
-- @param #POINT_VEC3 self
-- @param DCS#Vec3 Vec3 The Vec3 point.
-- @return Core.Point#POINT_VEC3 self
function POINT_VEC3:NewFromVec3( Vec3 )
local self = BASE:Inherit( self, COORDINATE:NewFromVec3( Vec3 ) ) -- Core.Point#POINT_VEC3
self:F2( self )
return self
end
--- Return the x coordinate of the POINT_VEC3.
-- @param #POINT_VEC3 self
-- @return #number The x coordinate.
function POINT_VEC3:GetX()
return self.x
end
--- Return the y coordinate of the POINT_VEC3.
-- @param #POINT_VEC3 self
-- @return #number The y coordinate.
function POINT_VEC3:GetY()
return self.y
end
--- Return the z coordinate of the POINT_VEC3.
-- @param #POINT_VEC3 self
-- @return #number The z coordinate.
function POINT_VEC3:GetZ()
return self.z
end
--- Set the x coordinate of the POINT_VEC3.
-- @param #POINT_VEC3 self
-- @param #number x The x coordinate.
-- @return #POINT_VEC3
function POINT_VEC3:SetX( x )
self.x = x
return self
end
--- Set the y coordinate of the POINT_VEC3.
-- @param #POINT_VEC3 self
-- @param #number y The y coordinate.
-- @return #POINT_VEC3
function POINT_VEC3:SetY( y )
self.y = y
return self
end
--- Set the z coordinate of the POINT_VEC3.
-- @param #POINT_VEC3 self
-- @param #number z The z coordinate.
-- @return #POINT_VEC3
function POINT_VEC3:SetZ( z )
self.z = z
return self
end
--- Add to the x coordinate of the POINT_VEC3.
-- @param #POINT_VEC3 self
-- @param #number x The x coordinate value to add to the current x coordinate.
-- @return #POINT_VEC3
function POINT_VEC3:AddX( x )
self.x = self.x + x
return self
end
--- Add to the y coordinate of the POINT_VEC3.
-- @param #POINT_VEC3 self
-- @param #number y The y coordinate value to add to the current y coordinate.
-- @return #POINT_VEC3
function POINT_VEC3:AddY( y )
self.y = self.y + y
return self
end
--- Add to the z coordinate of the POINT_VEC3.
-- @param #POINT_VEC3 self
-- @param #number z The z coordinate value to add to the current z coordinate.
-- @return #POINT_VEC3
function POINT_VEC3:AddZ( z )
self.z = self.z +z
return self
end
--- Return a random POINT_VEC3 within an Outer Radius and optionally NOT within an Inner Radius of the POINT_VEC3.
-- @param #POINT_VEC3 self
-- @param DCS#Distance OuterRadius
-- @param DCS#Distance InnerRadius
-- @return #POINT_VEC3
function POINT_VEC3:GetRandomPointVec3InRadius( OuterRadius, InnerRadius )
return POINT_VEC3:NewFromVec3( self:GetRandomVec3InRadius( OuterRadius, InnerRadius ) )
end
end
do -- POINT_VEC2
do
-- @type POINT_VEC2
--- @type POINT_VEC2
-- @field DCS#Distance x The x coordinate in meters.
-- @field DCS#Distance y the y coordinate in meters.
-- @extends Core.Point#COORDINATE
--- Defines a 2D point in the simulator. The height coordinate (if needed) will be the land height + an optional added height specified.
--
-- **DEPRECATED - PLEASE USE COORDINATE!**
--
-- ## POINT_VEC2 constructor
--
-- A new POINT_VEC2 instance can be created with:
@@ -3731,166 +3885,4 @@ do -- POINT_VEC2
return self
end
--- Create a new POINT_VEC2 object from Vec2 coordinates.
-- @param #POINT_VEC2 self
-- @param DCS#Vec2 Vec2 The Vec2 point.
-- @return Core.Point#POINT_VEC2 self
function POINT_VEC2:NewFromVec2( Vec2, LandHeightAdd )
local LandHeight = land.getHeight( Vec2 )
LandHeightAdd = LandHeightAdd or 0
LandHeight = LandHeight + LandHeightAdd
local self = BASE:Inherit( self, COORDINATE:NewFromVec2( Vec2, LandHeightAdd ) ) -- #POINT_VEC2
self:F2( self )
return self
end
--- Create a new POINT_VEC2 object from Vec3 coordinates.
-- @param #POINT_VEC2 self
-- @param DCS#Vec3 Vec3 The Vec3 point.
-- @return Core.Point#POINT_VEC2 self
function POINT_VEC2:NewFromVec3( Vec3 )
local self = BASE:Inherit( self, COORDINATE:NewFromVec3( Vec3 ) ) -- #POINT_VEC2
self:F2( self )
return self
end
--- Return the x coordinate of the POINT_VEC2.
-- @param #POINT_VEC2 self
-- @return #number The x coordinate.
function POINT_VEC2:GetX()
return self.x
end
--- Return the y coordinate of the POINT_VEC2.
-- @param #POINT_VEC2 self
-- @return #number The y coordinate.
function POINT_VEC2:GetY()
return self.z
end
--- Set the x coordinate of the POINT_VEC2.
-- @param #POINT_VEC2 self
-- @param #number x The x coordinate.
-- @return #POINT_VEC2
function POINT_VEC2:SetX( x )
self.x = x
return self
end
--- Set the y coordinate of the POINT_VEC2.
-- @param #POINT_VEC2 self
-- @param #number y The y coordinate.
-- @return #POINT_VEC2
function POINT_VEC2:SetY( y )
self.z = y
return self
end
--- Return Return the Lat(itude) coordinate of the POINT_VEC2 (ie: (parent)POINT_VEC3.x).
-- @param #POINT_VEC2 self
-- @return #number The x coordinate.
function POINT_VEC2:GetLat()
return self.x
end
--- Set the Lat(itude) coordinate of the POINT_VEC2 (ie: POINT_VEC3.x).
-- @param #POINT_VEC2 self
-- @param #number x The x coordinate.
-- @return #POINT_VEC2
function POINT_VEC2:SetLat( x )
self.x = x
return self
end
--- Return the Lon(gitude) coordinate of the POINT_VEC2 (ie: (parent)POINT_VEC3.z).
-- @param #POINT_VEC2 self
-- @return #number The y coordinate.
function POINT_VEC2:GetLon()
return self.z
end
--- Set the Lon(gitude) coordinate of the POINT_VEC2 (ie: POINT_VEC3.z).
-- @param #POINT_VEC2 self
-- @param #number y The y coordinate.
-- @return #POINT_VEC2
function POINT_VEC2:SetLon( z )
self.z = z
return self
end
--- Return the altitude (height) of the land at the POINT_VEC2.
-- @param #POINT_VEC2 self
-- @return #number The land altitude.
function POINT_VEC2:GetAlt()
return self.y ~= 0 or land.getHeight( { x = self.x, y = self.z } )
end
--- Set the altitude of the POINT_VEC2.
-- @param #POINT_VEC2 self
-- @param #number Altitude The land altitude. If nothing (nil) is given, then the current land altitude is set.
-- @return #POINT_VEC2
function POINT_VEC2:SetAlt( Altitude )
self.y = Altitude or land.getHeight( { x = self.x, y = self.z } )
return self
end
--- Add to the x coordinate of the POINT_VEC2.
-- @param #POINT_VEC2 self
-- @param #number x The x coordinate.
-- @return #POINT_VEC2
function POINT_VEC2:AddX( x )
self.x = self.x + x
return self
end
--- Add to the y coordinate of the POINT_VEC2.
-- @param #POINT_VEC2 self
-- @param #number y The y coordinate.
-- @return #POINT_VEC2
function POINT_VEC2:AddY( y )
self.z = self.z + y
return self
end
--- Add to the current land height an altitude.
-- @param #POINT_VEC2 self
-- @param #number Altitude The Altitude to add. If nothing (nil) is given, then the current land altitude is set.
-- @return #POINT_VEC2
function POINT_VEC2:AddAlt( Altitude )
self.y = land.getHeight( { x = self.x, y = self.z } ) + Altitude or 0
return self
end
--- Return a random POINT_VEC2 within an Outer Radius and optionally NOT within an Inner Radius of the POINT_VEC2.
-- @param #POINT_VEC2 self
-- @param DCS#Distance OuterRadius
-- @param DCS#Distance InnerRadius
-- @return #POINT_VEC2
function POINT_VEC2:GetRandomPointVec2InRadius( OuterRadius, InnerRadius )
self:F2( { OuterRadius, InnerRadius } )
return POINT_VEC2:NewFromVec2( self:GetRandomVec2InRadius( OuterRadius, InnerRadius ) )
end
-- TODO: Check this to replace
--- Calculate the distance from a reference @{#POINT_VEC2}.
-- @param #POINT_VEC2 self
-- @param #POINT_VEC2 PointVec2Reference The reference @{#POINT_VEC2}.
-- @return DCS#Distance The distance from the reference @{#POINT_VEC2} in meters.
function POINT_VEC2:DistanceFromPointVec2( PointVec2Reference )
self:F2( PointVec2Reference )
local Distance = ( ( PointVec2Reference.x - self.x ) ^ 2 + ( PointVec2Reference.z - self.z ) ^2 ) ^ 0.5
self:T2( Distance )
return Distance
end
end

View File

@@ -289,7 +289,14 @@ do -- SET_BASE
-- Debug info.
--self:T2( { ObjectName = ObjectName, Object = Object } )
-- Error ahndling
if not ObjectName or ObjectName == "" then
self:E("SET_BASE:Add - Invalid ObjectName handed")
self:E({ObjectName=ObjectName, Object=Object})
return self
end
-- Ensure that the existing element is removed from the Set before a new one is inserted to the Set
if self.Set[ObjectName] then
self:Remove( ObjectName, true )
@@ -524,6 +531,21 @@ do -- SET_BASE
return self.SomeIteratorLimit or self:Count()
end
--- Get max threat level of all objects in the SET.
-- @param #SET_BASE self
-- @return #number Max threat level found.
function SET_BASE:GetThreatLevelMax()
local ThreatMax = 0
for _,_unit in pairs(self.Set or {}) do
local unit = _unit -- Wrapper.Unit#UNIT
local threat = unit.GetThreatLevel and unit:GetThreatLevel() or 0
if threat > ThreatMax then
ThreatMax = threat
end
end
return ThreatMax
end
--- Filters for the defined collection.
-- @param #SET_BASE self
@@ -607,14 +629,14 @@ do -- SET_BASE
return self
end
--- Iterate the SET_BASE while identifying the nearest object in the set from a @{Core.Point#POINT_VEC2}.
--- Iterate the SET_BASE while identifying the nearest object in the set from a @{Core.Point#COORDINATE}.
-- @param #SET_BASE self
-- @param Core.Point#POINT_VEC2 PointVec2 A @{Core.Point#COORDINATE} or @{Core.Point#POINT_VEC2} object (but **not** a simple DCS#Vec2!) from where to evaluate the closest object in the set.
-- @param Core.Point#COORDINATE Coordinate A @{Core.Point#COORDINATE} object (but **not** a simple DCS#Vec2!) from where to evaluate the closest object in the set.
-- @return Core.Base#BASE The closest object.
-- @usage
-- myset:FindNearestObjectFromPointVec2( ZONE:New("Test Zone"):GetCoordinate() )
function SET_BASE:FindNearestObjectFromPointVec2( PointVec2 )
--self:F2( PointVec2 )
function SET_BASE:FindNearestObjectFromPointVec2( Coordinate )
--self:F2( Coordinate )
local NearestObject = nil
local ClosestDistance = nil
@@ -622,9 +644,9 @@ do -- SET_BASE
for ObjectID, ObjectData in pairs( self.Set ) do
if NearestObject == nil then
NearestObject = ObjectData
ClosestDistance = PointVec2:DistanceFromPointVec2( ObjectData:GetCoordinate() )
ClosestDistance = Coordinate:DistanceFromPointVec2( ObjectData:GetCoordinate() )
else
local Distance = PointVec2:DistanceFromPointVec2( ObjectData:GetCoordinate() )
local Distance = Coordinate:DistanceFromPointVec2( ObjectData:GetCoordinate() )
if Distance < ClosestDistance then
NearestObject = ObjectData
ClosestDistance = Distance
@@ -1220,12 +1242,12 @@ do
return GroupFound
end
--- Iterate the SET_GROUP while identifying the nearest object from a @{Core.Point#POINT_VEC2}.
--- Iterate the SET_GROUP while identifying the nearest object from a @{Core.Point#COORDINATE}.
-- @param #SET_GROUP self
-- @param Core.Point#POINT_VEC2 PointVec2 A @{Core.Point#POINT_VEC2} object from where to evaluate the closest object in the set.
-- @param Core.Point#COORDINATE Coordinate A @{Core.Point#COORDINATE} object from where to evaluate the closest object in the set.
-- @return Wrapper.Group#GROUP The closest group.
function SET_GROUP:FindNearestGroupFromPointVec2( PointVec2 )
--self:F2( PointVec2 )
function SET_GROUP:FindNearestGroupFromPointVec2( Coordinate )
--self:F2( Coordinate )
local NearestGroup = nil -- Wrapper.Group#GROUP
local ClosestDistance = nil
@@ -1235,9 +1257,9 @@ do
for ObjectID, ObjectData in pairs( Set ) do
if NearestGroup == nil then
NearestGroup = ObjectData
ClosestDistance = PointVec2:DistanceFromPointVec2( ObjectData:GetCoordinate() )
ClosestDistance = Coordinate:DistanceFromPointVec2( ObjectData:GetCoordinate() )
else
local Distance = PointVec2:DistanceFromPointVec2( ObjectData:GetCoordinate() )
local Distance = Coordinate:DistanceFromPointVec2( ObjectData:GetCoordinate() )
if Distance < ClosestDistance then
NearestGroup = ObjectData
ClosestDistance = Distance
@@ -1538,6 +1560,13 @@ do
local size = 1
if Event.IniDCSGroup then
size = Event.IniDCSGroup:getSize()
elseif Event.IniDCSGroupName then
local grp = Group.getByName(Event.IniDCSGroupName)
if grp then
size = grp:getSize()
end
elseif Object:IsAlive() then
size = Object:CountAliveUnits()
end
if size == 1 then -- Only remove if the last unit of the group was destroyed.
self:Remove( ObjectName )
@@ -1709,7 +1738,7 @@ do
--- Iterate the SET_GROUP and return true if all the @{Wrapper.Group#GROUP} are completely in the @{Core.Zone#ZONE}
-- @param #SET_GROUP self
-- @param Core.Zone#ZONE ZoneObject The Zone to be tested for.
-- @param Core.Zone#ZONE Zone The Zone to be tested for.
-- @return #boolean true if all the @{Wrapper.Group#GROUP} are completely in the @{Core.Zone#ZONE}, false otherwise
-- @usage
-- local MyZone = ZONE:New("Zone1")
@@ -1756,7 +1785,7 @@ do
--- Iterate the SET_GROUP and return true if at least one of the @{Wrapper.Group#GROUP} is completely inside the @{Core.Zone#ZONE}
-- @param #SET_GROUP self
-- @param Core.Zone#ZONE ZoneObject The Zone to be tested for.
-- @param Core.Zone#ZONE Zone The Zone to be tested for.
-- @return #boolean true if at least one of the @{Wrapper.Group#GROUP} is completely inside the @{Core.Zone#ZONE}, false otherwise.
-- @usage
-- local MyZone = ZONE:New("Zone1")
@@ -1781,7 +1810,7 @@ do
--- Iterate the SET_GROUP and return true if at least one @{#UNIT} of one @{Wrapper.Group#GROUP} of the @{#SET_GROUP} is in @{Core.Zone}
-- @param #SET_GROUP self
-- @param Core.Zone#ZONE ZoneObject The Zone to be tested for.
-- @param Core.Zone#ZONE Zone The Zone to be tested for.
-- @return #boolean true if at least one of the @{Wrapper.Group#GROUP} is partly or completely inside the @{Core.Zone#ZONE}, false otherwise.
-- @usage
-- local MyZone = ZONE:New("Zone1")
@@ -1807,7 +1836,7 @@ do
--- Iterate the SET_GROUP and return true if at least one @{Wrapper.Group#GROUP} of the @{#SET_GROUP} is partly in @{Core.Zone}.
-- Will return false if a @{Wrapper.Group#GROUP} is fully in the @{Core.Zone}
-- @param #SET_GROUP self
-- @param Core.Zone#ZONE ZoneObject The Zone to be tested for.
-- @param Core.Zone#ZONE Zone The Zone to be tested for.
-- @return #boolean true if at least one of the @{Wrapper.Group#GROUP} is partly or completely inside the @{Core.Zone#ZONE}, false otherwise.
-- @usage
-- local MyZone = ZONE:New("Zone1")
@@ -1842,7 +1871,7 @@ do
-- This could also be achieved with `not SET_GROUP:AnyPartlyInZone(Zone)`, but it's easier for the
-- mission designer to add a dedicated method
-- @param #SET_GROUP self
-- @param Core.Zone#ZONE ZoneObject The Zone to be tested for.
-- @param Core.Zone#ZONE Zone The Zone to be tested for.
-- @return #boolean true if no @{Wrapper.Group#GROUP} is inside the @{Core.Zone#ZONE} in any way, false otherwise.
-- @usage
-- local MyZone = ZONE:New("Zone1")
@@ -1869,7 +1898,7 @@ do
-- That could easily be done with SET_GROUP:ForEachGroupCompletelyInZone(), but this function
-- provides an easy to use shortcut...
-- @param #SET_GROUP self
-- @param Core.Zone#ZONE ZoneObject The Zone to be tested for.
-- @param Core.Zone#ZONE Zone The Zone to be tested for.
-- @return #number the number of GROUPs completely in the Zone
-- @usage
-- local MyZone = ZONE:New("Zone1")
@@ -1891,7 +1920,7 @@ do
--- Iterate the SET_GROUP and count how many UNITs are completely in the Zone
-- @param #SET_GROUP self
-- @param Core.Zone#ZONE ZoneObject The Zone to be tested for.
-- @param Core.Zone#ZONE Zone The Zone to be tested for.
-- @return #number the number of GROUPs completely in the Zone
-- @usage
-- local MyZone = ZONE:New("Zone1")
@@ -2490,6 +2519,35 @@ do -- SET_UNIT
)
return self
end
--- Builds a set of units which belong to groups with certain **group names**.
-- @param #SET_UNIT self
-- @param #string Prefixes The (partial) group names to look for. Can be a single string or a table of strings.
-- @return #SET_UNIT self
function SET_UNIT:FilterGroupPrefixes(Prefixes)
if type(Prefixes) == "string" then
Prefixes = {Prefixes}
end
self:FilterFunction(
function(unit,prefixes)
local outcome = false
if unit then
local grp = unit:GetGroup()
local gname = grp ~= nil and grp:GetName() or "none"
for _,_fix in pairs(prefixes or {}) do
if string.find(gname,_fix) then
outcome = true
break
end
end
else
return false
end
return outcome
end, Prefixes
)
return self
end
--- Builds a set of units having a radar of give types.
-- All the units having a radar of a given type will be included within the set.
@@ -2706,7 +2764,7 @@ do -- SET_UNIT
--- Check if no element of the SET_UNIT is in the Zone.
-- @param #SET_UNIT self
-- @param Core.Zone#ZONE ZoneObject The Zone to be tested for.
-- @param Core.Zone#ZONE Zone The Zone to be tested for.
-- @return #boolean
function SET_UNIT:IsNotInZone( Zone )
@@ -3746,7 +3804,7 @@ do -- SET_STATIC
--- Check if no element of the SET_STATIC is in the Zone.
-- @param #SET_STATIC self
-- @param Core.Zone#ZONE ZoneObject The Zone to be tested for.
-- @param Core.Zone#ZONE Zone The Zone to be tested for.
-- @return #boolean
function SET_STATIC:IsNotInZone( Zone )
@@ -4405,6 +4463,35 @@ do -- SET_CLIENT
end
return self
end
--- Builds a set of clients which belong to groups with certain **group names**.
-- @param #SET_CLIENT self
-- @param #string Prefixes The (partial) group names to look for. Can be anywhere in the group name. Can be a single string or a table of strings.
-- @return #SET_CLIENT self
function SET_CLIENT:FilterGroupPrefixes(Prefixes)
if type(Prefixes) == "string" then
Prefixes = {Prefixes}
end
self:FilterFunction(
function(unit,prefixes)
local outcome = false
if unit then
local grp = unit:GetGroup()
local gname = grp ~= nil and grp:GetName() or "none"
for _,_fix in pairs(prefixes or {}) do
if string.find(gname,_fix) then
outcome = true
break
end
end
else
return false
end
return outcome
end, Prefixes
)
return self
end
--- Builds a set of clients that are only active.
-- Only the clients that are active will be included within the set.
@@ -4582,6 +4669,16 @@ do -- SET_CLIENT
end
return self
end
--- Make the SET handle CA slots **only** (GROUND units used by any player). Needs active filtering with `FilterStart()`
-- @param #SET_CLIENT self
-- @return #SET_CLIENT self
function SET_CLIENT:HandleCASlots()
self:HandleEvent(EVENTS.PlayerEnterUnit,SET_CLIENT._EventPlayerEnterUnit)
self:HandleEvent(EVENTS.PlayerLeaveUnit,SET_CLIENT._EventPlayerLeaveUnit)
self:FilterFunction(function(client) if client and client:IsAlive() and client:IsGround() then return true else return false end end)
return self
end
--- Handles the Database to check on an event (birth) that the Object was added in the Database.
-- This is required, because sometimes the _DATABASE birth event gets called later than the SET_BASE birth event!
@@ -5364,6 +5461,7 @@ do -- SET_AIRBASE
Airbases = {},
Filter = {
Coalitions = nil,
Zones = nil,
},
FilterMeta = {
Coalitions = {
@@ -5515,6 +5613,31 @@ do -- SET_AIRBASE
end
return self
end
--- Builds a set of airbase objects in zones.
-- @param #SET_AIRBASE self
-- @param #table Zones Table of Core.Zone#ZONE Zone objects, or a Core.Set#SET_ZONE
-- @return #SET_AIRBASE self
function SET_AIRBASE:FilterZones( Zones )
if not self.Filter.Zones then
self.Filter.Zones = {}
end
local zones = {}
if Zones.ClassName and Zones.ClassName == "SET_ZONE" then
zones = Zones.Set
elseif type( Zones ) ~= "table" or (type( Zones ) == "table" and Zones.ClassName ) then
self:E("***** FilterZones needs either a table of ZONE Objects or a SET_ZONE as parameter!")
return self
else
zones = Zones
end
for _,Zone in pairs( zones ) do
local zonename = Zone:GetName()
--self:T((zonename)
self.Filter.Zones[zonename] = Zone
end
return self
end
--- Starts the filtering.
-- @param #SET_AIRBASE self
@@ -5605,14 +5728,14 @@ do -- SET_AIRBASE
return self
end
--- Iterate the SET_AIRBASE while identifying the nearest @{Wrapper.Airbase#AIRBASE} from a @{Core.Point#POINT_VEC2}.
--- Iterate the SET_AIRBASE while identifying the nearest @{Wrapper.Airbase#AIRBASE} from a @{Core.Point#COORDINATE}.
-- @param #SET_AIRBASE self
-- @param Core.Point#POINT_VEC2 PointVec2 A @{Core.Point#POINT_VEC2} object from where to evaluate the closest @{Wrapper.Airbase#AIRBASE}.
-- @param Core.Point#COORDINATE Coordinate A @{Core.Point#COORDINATE} object from where to evaluate the closest @{Wrapper.Airbase#AIRBASE}.
-- @return Wrapper.Airbase#AIRBASE The closest @{Wrapper.Airbase#AIRBASE}.
function SET_AIRBASE:FindNearestAirbaseFromPointVec2( PointVec2 )
--self:F2( PointVec2 )
function SET_AIRBASE:FindNearestAirbaseFromPointVec2( Coordinate )
--self:F2( Coordinate )
local NearestAirbase = self:FindNearestObjectFromPointVec2( PointVec2 )
local NearestAirbase = self:FindNearestObjectFromPointVec2( Coordinate )
return NearestAirbase
end
@@ -5653,6 +5776,20 @@ do -- SET_AIRBASE
--self:T(( { "Evaluated Category", MAirbaseCategory } )
MAirbaseInclude = MAirbaseInclude and MAirbaseCategory
end
if self.Filter.Zones and MAirbaseInclude then
local MAirbaseZone = false
for ZoneName, Zone in pairs( self.Filter.Zones ) do
--self:T(( "Zone:", ZoneName )
local coord = MAirbase:GetCoordinate()
if coord and Zone:IsCoordinateInZone(coord) then
MAirbaseZone = true
end
--self:T(( { "Evaluated Zone", MSceneryZone } )
end
MAirbaseInclude = MAirbaseInclude and MAirbaseZone
end
end
if self.Filter.Functions and MAirbaseInclude then
@@ -5928,17 +6065,19 @@ do -- SET_CARGO
return self
end
--- (R2.1) Iterate the SET_CARGO while identifying the nearest @{Cargo.Cargo#CARGO} from a @{Core.Point#POINT_VEC2}.
--- (R2.1) Iterate the SET_CARGO while identifying the nearest @{Cargo.Cargo#CARGO} from a @{Core.Point#COORDINATE}.
-- @param #SET_CARGO self
-- @param Core.Point#POINT_VEC2 PointVec2 A @{Core.Point#POINT_VEC2} object from where to evaluate the closest @{Cargo.Cargo#CARGO}.
-- @param Core.Point#COORDINATE Coordinate A @{Core.Point#COORDINATE} object from where to evaluate the closest @{Cargo.Cargo#CARGO}.
-- @return Cargo.Cargo#CARGO The closest @{Cargo.Cargo#CARGO}.
function SET_CARGO:FindNearestCargoFromPointVec2( PointVec2 ) -- R2.1
--self:F2( PointVec2 )
function SET_CARGO:FindNearestCargoFromPointVec2( Coordinate ) -- R2.1
--self:F2( Coordinate )
local NearestCargo = self:FindNearestObjectFromPointVec2( PointVec2 )
local NearestCargo = self:FindNearestObjectFromPointVec2( Coordinate )
return NearestCargo
end
---
-- @param #SET_CARGO self
function SET_CARGO:FirstCargoWithState( State )
local FirstCargo = nil
@@ -5953,6 +6092,8 @@ do -- SET_CARGO
return FirstCargo
end
---
-- @param #SET_CARGO self
function SET_CARGO:FirstCargoWithStateAndNotDeployed( State )
local FirstCargo = nil
@@ -7936,22 +8077,31 @@ do -- SET_OPSGROUP
--- Handles the OnBirth event for the Set.
-- @param #SET_OPSGROUP self
-- @param Core.Event#EVENTDATA Event Event data.
function SET_OPSGROUP:_EventOnBirth( Event )
function SET_OPSGROUP:_EventOnBirth(Event)
--self:F3( { Event } )
if Event.IniDCSUnit and Event.IniDCSGroup then
local DCSgroup=Event.IniDCSGroup --DCS#Group
local DCSgroup = Event.IniDCSGroup --DCS#Group
if DCSgroup:getInitialSize() == DCSgroup:getSize() then -- This seems to be not a good check as even for the first birth event, getSize returns the total number of units in the group.
local groupname, group = self:AddInDatabase( Event )
if group and group:CountAliveUnits()==DCSgroup:getInitialSize() then
if group and self:IsIncludeObject( group ) then
self:Add( groupname, group )
end
-- group:CountAliveUnits() alternative as this fails for Respawn/Teleport
local CountAliveActive = 0
for index, data in pairs(DCSgroup:getUnits()) do
if data:isExist() and data:isActive() then
CountAliveActive = CountAliveActive + 1
end
end
if DCSgroup:getInitialSize() == DCSgroup:getSize() then
local groupname, group = self:AddInDatabase(Event)
-- group:CountAliveUnits() alternative
if group and CountAliveActive == DCSgroup:getInitialSize() then
if group and self:IsIncludeObject(group) then
self:Add(groupname, group)
end
end
end
end
end
end
@@ -7962,7 +8112,7 @@ do -- SET_OPSGROUP
function SET_OPSGROUP:_EventOnDeadOrCrash( Event )
--self:F( { Event } )
if Event.IniDCSUnit then
if Event.IniDCSGroup then
local ObjectName, Object = self:FindInDatabase( Event )
if ObjectName then
if Event.IniDCSGroup:getSize() == 1 then -- Only remove if the last unit of the group was destroyed.
@@ -8355,14 +8505,17 @@ do -- SET_SCENERY
-- @param #SET_SCENERY self
-- @return Core.Point#COORDINATE The center coordinate of all the objects in the set.
function SET_SCENERY:GetCoordinate()
--[[
local Coordinate = COORDINATE:New({0,0,0})
local Item = self:GetRandomSurely()
if Item then
Coordinate:GetCoordinate()
end
--]]
local Coordinate = self:GetFirst():GetCoordinate()
local x1 = Coordinate.x
local x2 = Coordinate.x
@@ -8602,7 +8755,6 @@ do -- SET_DYNAMICCARGO
-- @field #SET_DYNAMICCARGO SET_DYNAMICCARGO
SET_DYNAMICCARGO = {
ClassName = "SET_DYNAMICCARGO",
Filter = {},
Set = {},
List = {},
Index = {},

View File

@@ -494,7 +494,7 @@ do -- SETTINGS
return (self.A2ASystem and self.A2ASystem == "MGRS") or (not self.A2ASystem and _SETTINGS:IsA2A_MGRS())
end
-- @param #SETTINGS self
--- @param #SETTINGS self
-- @param Wrapper.Group#GROUP MenuGroup Group for which to add menus.
-- @param #table RootMenu Root menu table
-- @return #SETTINGS
@@ -948,49 +948,49 @@ do -- SETTINGS
return self
end
-- @param #SETTINGS self
--- @param #SETTINGS self
function SETTINGS:A2GMenuSystem( MenuGroup, RootMenu, A2GSystem )
self.A2GSystem = A2GSystem
MESSAGE:New( string.format( "Settings: Default A2G coordinate system set to %s for all players!", A2GSystem ), 5 ):ToAll()
self:SetSystemMenu( MenuGroup, RootMenu )
end
-- @param #SETTINGS self
--- @param #SETTINGS self
function SETTINGS:A2AMenuSystem( MenuGroup, RootMenu, A2ASystem )
self.A2ASystem = A2ASystem
MESSAGE:New( string.format( "Settings: Default A2A coordinate system set to %s for all players!", A2ASystem ), 5 ):ToAll()
self:SetSystemMenu( MenuGroup, RootMenu )
end
-- @param #SETTINGS self
--- @param #SETTINGS self
function SETTINGS:MenuLL_DDM_Accuracy( MenuGroup, RootMenu, LL_Accuracy )
self.LL_Accuracy = LL_Accuracy
MESSAGE:New( string.format( "Settings: Default LL accuracy set to %s for all players!", LL_Accuracy ), 5 ):ToAll()
self:SetSystemMenu( MenuGroup, RootMenu )
end
-- @param #SETTINGS self
--- @param #SETTINGS self
function SETTINGS:MenuMGRS_Accuracy( MenuGroup, RootMenu, MGRS_Accuracy )
self.MGRS_Accuracy = MGRS_Accuracy
MESSAGE:New( string.format( "Settings: Default MGRS accuracy set to %s for all players!", MGRS_Accuracy ), 5 ):ToAll()
self:SetSystemMenu( MenuGroup, RootMenu )
end
-- @param #SETTINGS self
--- @param #SETTINGS self
function SETTINGS:MenuMWSystem( MenuGroup, RootMenu, MW )
self.Metric = MW
MESSAGE:New( string.format( "Settings: Default measurement format set to %s for all players!", MW and "Metric" or "Imperial" ), 5 ):ToAll()
self:SetSystemMenu( MenuGroup, RootMenu )
end
-- @param #SETTINGS self
--- @param #SETTINGS self
function SETTINGS:MenuMessageTimingsSystem( MenuGroup, RootMenu, MessageType, MessageTime )
self:SetMessageTime( MessageType, MessageTime )
MESSAGE:New( string.format( "Settings: Default message time set for %s to %d.", MessageType, MessageTime ), 5 ):ToAll()
end
do
-- @param #SETTINGS self
--- @param #SETTINGS self
function SETTINGS:MenuGroupA2GSystem( PlayerUnit, PlayerGroup, PlayerName, A2GSystem )
--BASE:E( {PlayerUnit:GetName(), A2GSystem } )
self.A2GSystem = A2GSystem
@@ -1001,7 +1001,7 @@ do -- SETTINGS
end
end
-- @param #SETTINGS self
--- @param #SETTINGS self
function SETTINGS:MenuGroupA2ASystem( PlayerUnit, PlayerGroup, PlayerName, A2ASystem )
self.A2ASystem = A2ASystem
MESSAGE:New( string.format( "Settings: A2A format set to %s for player %s.", A2ASystem, PlayerName ), 5 ):ToGroup( PlayerGroup )
@@ -1011,7 +1011,7 @@ do -- SETTINGS
end
end
-- @param #SETTINGS self
--- @param #SETTINGS self
function SETTINGS:MenuGroupLL_DDM_AccuracySystem( PlayerUnit, PlayerGroup, PlayerName, LL_Accuracy )
self.LL_Accuracy = LL_Accuracy
MESSAGE:New( string.format( "Settings: LL format accuracy set to %d decimal places for player %s.", LL_Accuracy, PlayerName ), 5 ):ToGroup( PlayerGroup )
@@ -1021,7 +1021,7 @@ do -- SETTINGS
end
end
-- @param #SETTINGS self
--- @param #SETTINGS self
function SETTINGS:MenuGroupMGRS_AccuracySystem( PlayerUnit, PlayerGroup, PlayerName, MGRS_Accuracy )
self.MGRS_Accuracy = MGRS_Accuracy
MESSAGE:New( string.format( "Settings: MGRS format accuracy set to %d for player %s.", MGRS_Accuracy, PlayerName ), 5 ):ToGroup( PlayerGroup )
@@ -1031,7 +1031,7 @@ do -- SETTINGS
end
end
-- @param #SETTINGS self
--- @param #SETTINGS self
function SETTINGS:MenuGroupMWSystem( PlayerUnit, PlayerGroup, PlayerName, MW )
self.Metric = MW
MESSAGE:New( string.format( "Settings: Measurement format set to %s for player %s.", MW and "Metric" or "Imperial", PlayerName ), 5 ):ToGroup( PlayerGroup )
@@ -1041,7 +1041,7 @@ do -- SETTINGS
end
end
-- @param #SETTINGS self
--- @param #SETTINGS self
function SETTINGS:MenuGroupMessageTimingsSystem( PlayerUnit, PlayerGroup, PlayerName, MessageType, MessageTime )
self:SetMessageTime( MessageType, MessageTime )
MESSAGE:New( string.format( "Settings: Default message time set for %s to %d.", MessageType, MessageTime ), 5 ):ToGroup( PlayerGroup )

View File

@@ -1081,7 +1081,7 @@ function SPAWN:InitRandomizeTemplate( SpawnTemplatePrefixTable )
self.SpawnRandomizeTemplate = true
for SpawnGroupID = 1, self.SpawnMaxGroups do
self:_RandomizeTemplate( SpawnGroupID )
self:_RandomizeTemplate( SpawnGroupID, RandomizePositionInZone )
end
return self
@@ -1093,6 +1093,7 @@ end
-- In other words, this method randomizes between a defined set of groups the template to be used for each new spawn of a group.
-- @param #SPAWN self
-- @param Core.Set#SET_GROUP SpawnTemplateSet A SET_GROUP object set, that contains the groups that are possible unit representatives of the group to be spawned.
-- @param #boolean RandomizePositionInZone If nil or true, also the position inside the selected random zone will be randomized. Set to false to use the center of the zone.
-- @return #SPAWN
-- @usage
--
@@ -1111,11 +1112,11 @@ end
-- Spawn_US_Platoon_Middle = SPAWN:New( 'US Tank Platoon Middle' ):InitLimit( 12, 150 ):SpawnScheduled( 200, 0.4 ):InitRandomizeTemplateSet( Spawn_US_PlatoonSet ):InitRandomizeRoute( 3, 3, 2000 )
-- Spawn_US_Platoon_Right = SPAWN:New( 'US Tank Platoon Right' ):InitLimit( 12, 150 ):SpawnScheduled( 200, 0.4 ):InitRandomizeTemplateSet( Spawn_US_PlatoonSet ):InitRandomizeRoute( 3, 3, 2000 )
--
function SPAWN:InitRandomizeTemplateSet( SpawnTemplateSet )
function SPAWN:InitRandomizeTemplateSet( SpawnTemplateSet,RandomizePositionInZone )
--self:F( { self.SpawnTemplatePrefix } )
local setnames = SpawnTemplateSet:GetSetNames()
self:InitRandomizeTemplate(setnames)
self:InitRandomizeTemplate(setnames,RandomizePositionInZone)
return self
end
@@ -1125,7 +1126,8 @@ end
-- but they will all follow the same Template route and have the same prefix name.
-- In other words, this method randomizes between a defined set of groups the template to be used for each new spawn of a group.
-- @param #SPAWN self
-- @param #string SpawnTemplatePrefixes A string or a list of string that contains the prefixes of the groups that are possible unit representatives of the group to be spawned.
-- @param #string SpawnTemplatePrefixes A string or a list of string that contains the prefixes of the groups that are possible unit representatives of the group to be spawned.
-- @param #boolean RandomizePositionInZone If nil or true, also the position inside the selected random zone will be randomized. Set to false to use the center of the zone.
-- @return #SPAWN
-- @usage
--
@@ -1141,12 +1143,12 @@ end
-- Spawn_US_Platoon_Middle = SPAWN:New( 'US Tank Platoon Middle' ):InitLimit( 12, 150 ):SpawnScheduled( 200, 0.4 ):InitRandomizeTemplatePrefixes( "US Tank Platoon Templates" ):InitRandomizeRoute( 3, 3, 2000 )
-- Spawn_US_Platoon_Right = SPAWN:New( 'US Tank Platoon Right' ):InitLimit( 12, 150 ):SpawnScheduled( 200, 0.4 ):InitRandomizeTemplatePrefixes( "US Tank Platoon Templates" ):InitRandomizeRoute( 3, 3, 2000 )
--
function SPAWN:InitRandomizeTemplatePrefixes( SpawnTemplatePrefixes ) -- R2.3
function SPAWN:InitRandomizeTemplatePrefixes( SpawnTemplatePrefixes, RandomizePositionInZone ) -- R2.3
--self:F( { self.SpawnTemplatePrefix } )
local SpawnTemplateSet = SET_GROUP:New():FilterPrefixes( SpawnTemplatePrefixes ):FilterOnce()
self:InitRandomizeTemplateSet( SpawnTemplateSet )
self:InitRandomizeTemplateSet( SpawnTemplateSet, RandomizePositionInZone )
return self
end
@@ -1166,6 +1168,7 @@ end
--- This method provides the functionality to randomize the spawning of the Groups at a given list of zones of different types.
-- @param #SPAWN self
-- @param #table SpawnZoneTable A table with @{Core.Zone} objects. If this table is given, then each spawn will be executed within the given list of @{Core.Zone}s objects.
-- @param #boolean RandomizePositionInZone If nil or true, also the position inside the selected random zone will be randomized. Set to false to use the center of the zone.
-- @return #SPAWN self
-- @usage
--
@@ -1178,7 +1181,7 @@ end
-- :InitRandomizeZones( ZoneTable )
-- :SpawnScheduled( 5, .5 )
--
function SPAWN:InitRandomizeZones( SpawnZoneTable )
function SPAWN:InitRandomizeZones( SpawnZoneTable, RandomizePositionInZone )
--self:F( { self.SpawnTemplatePrefix, SpawnZoneTable } )
local temptable = {}
@@ -1190,7 +1193,7 @@ function SPAWN:InitRandomizeZones( SpawnZoneTable )
self.SpawnRandomizeZones = true
for SpawnGroupID = 1, self.SpawnMaxGroups do
self:_RandomizeZones( SpawnGroupID )
self:_RandomizeZones( SpawnGroupID, RandomizePositionInZone )
end
return self
@@ -1275,6 +1278,7 @@ end
--- Respawn group after landing.
-- @param #SPAWN self
-- @param #number WaitingTime Wait this many seconds before despawning the alive group after landing. Defaults to 3 .
-- @return #SPAWN self
-- @usage
--
@@ -1282,15 +1286,16 @@ end
-- -- Re-SPAWN the Group(s) after each landing and Engine Shut-Down automatically.
-- SpawnRU_SU34 = SPAWN:New( 'Su-34' )
-- :InitRandomizeRoute( 1, 1, 3000 )
-- :InitRepeatOnLanding()
-- :InitRepeatOnLanding(20)
-- :Spawn()
--
function SPAWN:InitRepeatOnLanding()
function SPAWN:InitRepeatOnLanding(WaitingTime)
--self:F( { self.SpawnTemplatePrefix } )
self:InitRepeat()
self.RepeatOnEngineShutDown = false
self.RepeatOnLanding = true
self.RepeatOnLandingTime = (WaitingTime and WaitingTime > 3) and WaitingTime or 3
return self
end
@@ -1626,7 +1631,7 @@ function SPAWN:SpawnWithIndex( SpawnIndex, NoBirth )
if SpawnTemplate then
local PointVec3 = POINT_VEC3:New( SpawnTemplate.route.points[1].x, SpawnTemplate.route.points[1].alt, SpawnTemplate.route.points[1].y )
local PointVec3 = COORDINATE:New( SpawnTemplate.route.points[1].x, SpawnTemplate.route.points[1].alt, SpawnTemplate.route.points[1].y )
--self:T2( { "Current point of ", self.SpawnTemplatePrefix, PointVec3 } )
-- If RandomizePosition, then Randomize the formation in the zone band, keeping the template.
@@ -2028,12 +2033,10 @@ end
--
-- Spawn_Plane:SpawnAtAirbase( AIRBASE:FindByName( AIRBASE.Caucasus.Krymsk ), SPAWN.Takeoff.Cold, nil, AIRBASE.TerminalType.OpenBig )
--
function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalType, EmergencyAirSpawn, Parkingdata ) -- R2.2, R2.4
--self:F( { self.SpawnTemplatePrefix, SpawnAirbase, Takeoff, TakeoffAltitude, TerminalType } )
function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalType, EmergencyAirSpawn, Parkingdata )
-- Get position of airbase.
local PointVec3 = SpawnAirbase:GetCoordinate()
--self:T2( PointVec3 )
-- Set take off type. Default is hot.
Takeoff = Takeoff or SPAWN.Takeoff.Hot
@@ -2043,39 +2046,24 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
EmergencyAirSpawn = true
end
--self:F( { SpawnIndex = self.SpawnIndex } )
if self:_GetSpawnIndex( self.SpawnIndex + 1 ) then
-- Get group template.
local SpawnTemplate = self.SpawnGroups[self.SpawnIndex].SpawnTemplate
--self:F( { SpawnTemplate = SpawnTemplate } )
if SpawnTemplate then
-- Check if the aircraft with the specified SpawnIndex is already spawned.
-- If yes, ensure that the aircraft is spawned at the same aircraft spot.
local GroupAlive = self:GetGroupFromIndex( self.SpawnIndex )
--self:F( { GroupAlive = GroupAlive } )
-- Debug output
--self:T2( { "Current point of ", self.SpawnTemplatePrefix, SpawnAirbase } )
-- Template group, unit and its attributes.
local TemplateGroup = GROUP:FindByName( self.SpawnTemplatePrefix )
local TemplateUnit = TemplateGroup:GetUnit( 1 )
local group = GROUP:FindByName( self.SpawnTemplatePrefix )
local unit = group:GetUnit( 1 )
-- General category of spawned group.
local group = TemplateGroup
local istransport = group:HasAttribute( "Transports" ) and group:HasAttribute( "Planes" )
local isawacs = group:HasAttribute( "AWACS" )
local isfighter = group:HasAttribute( "Fighters" ) or group:HasAttribute( "Interceptors" ) or group:HasAttribute( "Multirole fighters" ) or (group:HasAttribute( "Bombers" ) and not group:HasAttribute( "Strategic bombers" ))
local isbomber = group:HasAttribute( "Strategic bombers" )
local istanker = group:HasAttribute( "Tankers" )
local ishelo = TemplateUnit:HasAttribute( "Helicopters" )
local ishelo = unit:HasAttribute( "Helicopters" )
-- Number of units in the group. With grouping this can actually differ from the template group size!
local nunits = #SpawnTemplate.units
@@ -2093,40 +2081,32 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
local AirbaseCategory = SpawnAirbase:GetAirbaseCategory()
--self:F( { AirbaseCategory = AirbaseCategory } )
-- Set airdromeId.
-- Set airdrome ID. For helipads and ships we need to add the helipad ID and linked unit.
-- Note, it is important not to set the airdrome ID for at least ships, because spawn will happen at origin of the map
if AirbaseCategory == Airbase.Category.SHIP then
SpawnPoint.linkUnit = AirbaseID
SpawnPoint.helipadId = AirbaseID
elseif AirbaseCategory == Airbase.Category.HELIPAD then
SpawnPoint.linkUnit = AirbaseID
SpawnPoint.helipadId = AirbaseID
elseif AirbaseCategory == Airbase.Category.AIRDROME then
else
SpawnPoint.airdromeId = AirbaseID
end
-- Set waypoint type/action.
SpawnPoint.alt = 0
SpawnPoint.type = GROUPTEMPLATE.Takeoff[Takeoff][1] -- type
SpawnPoint.alt = 0
SpawnPoint.type = GROUPTEMPLATE.Takeoff[Takeoff][1] -- type
SpawnPoint.action = GROUPTEMPLATE.Takeoff[Takeoff][2] -- action
-- Check if we spawn on ground.
local spawnonground = not (Takeoff == SPAWN.Takeoff.Air)
--self:T2( { spawnonground = spawnonground, TOtype = Takeoff, TOair = Takeoff == SPAWN.Takeoff.Air } )
-- Check where we actually spawn if we spawn on ground.
local spawnonship = false
local spawnonfarp = false
local spawnonrunway = false
local spawnonairport = false
if spawnonground then
if AirbaseCategory == Airbase.Category.SHIP then
spawnonship = true
elseif AirbaseCategory == Airbase.Category.HELIPAD then
spawnonfarp = true
elseif AirbaseCategory == Airbase.Category.AIRDROME then
spawnonairport = true
end
spawnonrunway = Takeoff == SPAWN.Takeoff.Runway
local autoparking=false
if SpawnAirbase.isAirdrome then
autoparking=false
else
autoparking=true
end
-- Array with parking spots coordinates.
@@ -2142,8 +2122,8 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
-- Set terminal type.
local termtype = TerminalType
if spawnonrunway then
if spawnonship then
if Takeoff==SPAWN.Takeoff.Runway then
if SpawnAirbase.isShip then
-- Looks like there are no runway spawn spots on the stennis!
if ishelo then
termtype = AIRBASE.TerminalType.HelicopterUsable
@@ -2163,34 +2143,31 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
local verysafe = false
-- Number of free parking spots at the airbase.
if spawnonship or spawnonfarp or spawnonrunway then
if autoparking then
-- These places work procedural and have some kind of build in queue ==> Less effort.
--self:T2( string.format( "Group %s is spawned on farp/ship/runway %s.", self.SpawnTemplatePrefix, SpawnAirbase:GetName() ) )
nfree = SpawnAirbase:GetFreeParkingSpotsNumber( termtype, true )
spots = SpawnAirbase:GetFreeParkingSpotsTable( termtype, true )
--[[
elseif Parkingdata~=nil then
-- Parking data explicitly set by user as input parameter.
-- Parking data explicitly set by user as input parameter. (This was commented out for some unknown reason. But I need it this way.)
nfree=#Parkingdata
spots=Parkingdata
]]
else
if ishelo then
if termtype == nil then
-- Helo is spawned. Try exclusive helo spots first.
--self:T2( string.format( "Helo group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), AIRBASE.TerminalType.HelicopterOnly ) )
spots = SpawnAirbase:FindFreeParkingSpotForAircraft( TemplateGroup, AIRBASE.TerminalType.HelicopterOnly, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, Parkingdata )
spots = SpawnAirbase:FindFreeParkingSpotForAircraft( group, AIRBASE.TerminalType.HelicopterOnly, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, Parkingdata )
nfree = #spots
if nfree < nunits then
-- Not enough helo ports. Let's try also other terminal types.
--self:T2( string.format( "Helo group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), AIRBASE.TerminalType.HelicopterUsable ) )
spots = SpawnAirbase:FindFreeParkingSpotForAircraft( TemplateGroup, AIRBASE.TerminalType.HelicopterUsable, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, Parkingdata )
spots = SpawnAirbase:FindFreeParkingSpotForAircraft( group, AIRBASE.TerminalType.HelicopterUsable, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, Parkingdata )
nfree = #spots
end
else
-- No terminal type specified. We try all spots except shelters.
--self:T2( string.format( "Helo group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), termtype ) )
spots = SpawnAirbase:FindFreeParkingSpotForAircraft( TemplateGroup, termtype, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, Parkingdata )
spots = SpawnAirbase:FindFreeParkingSpotForAircraft( group, termtype, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, Parkingdata )
nfree = #spots
end
else
@@ -2199,44 +2176,33 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
if isbomber or istransport or istanker or isawacs then
-- First we fill the potentially bigger spots.
--self:T2( string.format( "Transport/bomber group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), AIRBASE.TerminalType.OpenBig ) )
spots = SpawnAirbase:FindFreeParkingSpotForAircraft( TemplateGroup, AIRBASE.TerminalType.OpenBig, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, Parkingdata )
spots = SpawnAirbase:FindFreeParkingSpotForAircraft( group, AIRBASE.TerminalType.OpenBig, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, Parkingdata )
nfree = #spots
if nfree < nunits then
-- Now we try the smaller ones.
--self:T2( string.format( "Transport/bomber group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), AIRBASE.TerminalType.OpenMedOrBig ) )
spots = SpawnAirbase:FindFreeParkingSpotForAircraft( TemplateGroup, AIRBASE.TerminalType.OpenMedOrBig, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, Parkingdata )
spots = SpawnAirbase:FindFreeParkingSpotForAircraft( group, AIRBASE.TerminalType.OpenMedOrBig, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, Parkingdata )
nfree = #spots
end
else
--self:T2( string.format( "Fighter group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), AIRBASE.TerminalType.FighterAircraft ) )
spots = SpawnAirbase:FindFreeParkingSpotForAircraft( TemplateGroup, AIRBASE.TerminalType.FighterAircraft, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, Parkingdata )
spots = SpawnAirbase:FindFreeParkingSpotForAircraft( group, AIRBASE.TerminalType.FighterAircraft, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, Parkingdata )
nfree = #spots
end
else
-- Terminal type explicitly given.
--self:T2( string.format( "Plane group %s is at %s using terminal type %s.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), tostring( termtype ) ) )
spots = SpawnAirbase:FindFreeParkingSpotForAircraft( TemplateGroup, termtype, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, Parkingdata )
spots = SpawnAirbase:FindFreeParkingSpotForAircraft( group, termtype, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, Parkingdata )
nfree = #spots
end
end
end
-- Debug: Get parking data.
--[[
local parkingdata=SpawnAirbase:GetParkingSpotsTable(termtype)
--self:T2(string.format("Parking at %s, terminal type %s:", SpawnAirbase:GetName(), tostring(termtype)))
for _,_spot in pairs(parkingdata) do
--self:T2(string.format("%s, Termin Index = %3d, Term Type = %03d, Free = %5s, TOAC = %5s, Term ID0 = %3d, Dist2Rwy = %4d",
SpawnAirbase:GetName(), _spot.TerminalID, _spot.TerminalType,tostring(_spot.Free),tostring(_spot.TOAC),_spot.TerminalID0,_spot.DistToRwy))
end
--self:T2(string.format("%s at %s: free parking spots = %d - number of units = %d", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), nfree, nunits))
]]
-- Set this to true if not enough spots are available for emergency air start.
local _notenough = false
-- Need to differentiate some cases again.
if spawnonship or spawnonfarp or spawnonrunway then
if autoparking then
-- On free spot required in these cases.
if nfree >= 1 then
@@ -2254,7 +2220,7 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
_notenough = true
end
elseif spawnonairport then
else
if nfree >= nunits then
@@ -2276,13 +2242,10 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
self:E( string.format( "WARNING: Group %s has no parking spots at %s ==> air start!", self.SpawnTemplatePrefix, SpawnAirbase:GetName() ) )
-- Not enough parking spots at the airport ==> Spawn in air.
spawnonground = false
spawnonship = false
spawnonfarp = false
spawnonrunway = false
autoparking=false
-- Set waypoint type/action to turning point.
SpawnPoint.type = GROUPTEMPLATE.Takeoff[GROUP.Takeoff.Air][1] -- type = Turning Point
SpawnPoint.type = GROUPTEMPLATE.Takeoff[GROUP.Takeoff.Air][1] -- type = Turning Point
SpawnPoint.action = GROUPTEMPLATE.Takeoff[GROUP.Takeoff.Air][2] -- action = Turning Point
-- Adjust altitude to be 500-1000 m above the airbase.
@@ -2324,7 +2287,6 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
SpawnTemplate.parked = true
for UnitID = 1, nunits do
--self:T2( 'Before Translation SpawnTemplate.units[' .. UnitID .. '].x = ' .. SpawnTemplate.units[UnitID].x .. ', SpawnTemplate.units[' .. UnitID .. '].y = ' .. SpawnTemplate.units[UnitID].y )
-- Template of the current unit.
local UnitTemplate = SpawnTemplate.units[UnitID]
@@ -2340,9 +2302,7 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
if spawnonground then
-- Ships and FARPS seem to have a build in queue.
if spawnonship or spawnonfarp or spawnonrunway then
--self:T2( string.format( "Group %s spawning at farp, ship or runway %s.", self.SpawnTemplatePrefix, SpawnAirbase:GetName() ) )
if autoparking then
-- Spawn on ship. We take only the position of the ship.
SpawnTemplate.units[UnitID].x = PointVec3.x -- TX
@@ -2351,20 +2311,15 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
else
--self:T2( string.format( "Group %s spawning at airbase %s on parking spot id %d", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), parkingindex[UnitID] ) )
-- Get coordinates of parking spot.
SpawnTemplate.units[UnitID].x = parkingspots[UnitID].x
SpawnTemplate.units[UnitID].y = parkingspots[UnitID].z
SpawnTemplate.units[UnitID].alt = parkingspots[UnitID].y
-- parkingspots[UnitID]:MarkToAll(string.format("Group %s spawning at airbase %s on parking spot id %d", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), parkingindex[UnitID]))
end
else
--self:T2( string.format( "Group %s spawning in air at %s.", self.SpawnTemplatePrefix, SpawnAirbase:GetName() ) )
-- Spawn in air as requested initially. Original template orientation is perserved, altitude is already correctly set.
SpawnTemplate.units[UnitID].x = TX
SpawnTemplate.units[UnitID].y = TY
@@ -2378,11 +2333,6 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
if parkingindex[UnitID] then
UnitTemplate.parking = parkingindex[UnitID]
end
-- Debug output.
--self:T2( string.format( "Group %s unit number %d: Parking = %s", self.SpawnTemplatePrefix, UnitID, tostring( UnitTemplate.parking ) ) )
--self:T2( string.format( "Group %s unit number %d: Parking ID = %s", self.SpawnTemplatePrefix, UnitID, tostring( UnitTemplate.parking_id ) ) )
--self:T2( 'After Translation SpawnTemplate.units[' .. UnitID .. '].x = ' .. SpawnTemplate.units[UnitID].x .. ', SpawnTemplate.units[' .. UnitID .. '].y = ' .. SpawnTemplate.units[UnitID].y )
end
end
@@ -2402,14 +2352,15 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
-- When spawned in the air, we need to generate a Takeoff Event.
if Takeoff == GROUP.Takeoff.Air then
for UnitID, UnitSpawned in pairs( GroupSpawned:GetUnits() ) do
SCHEDULER:New( nil, BASE.CreateEventTakeoff, { GroupSpawned, timer.getTime(), UnitSpawned:GetDCSObject() }, 5 )
--SCHEDULER:New( nil, BASE.CreateEventTakeoff, { GroupSpawned, timer.getTime(), UnitSpawned:GetDCSObject() }, 5 ) --No need to create a new SCHEDULER instance every time!
self:ScheduleOnce(5, BASE.CreateEventTakeoff, {GroupSpawned, timer.getTime(), UnitSpawned:GetDCSObject()})
end
end
-- Check if we accidentally spawned on the runway. Needs to be schedules, because group is not immidiately alive.
if Takeoff ~= SPAWN.Takeoff.Runway and Takeoff ~= SPAWN.Takeoff.Air and spawnonairport then
SCHEDULER:New( nil, AIRBASE.CheckOnRunWay, { SpawnAirbase, GroupSpawned, 75, true }, 1.0 )
end
--if Takeoff ~= SPAWN.Takeoff.Runway and Takeoff ~= SPAWN.Takeoff.Air and spawnonairport then
-- SCHEDULER:New( nil, AIRBASE.CheckOnRunWay, { SpawnAirbase, GroupSpawned, 75, true }, 1.0 )
--end
return GroupSpawned
end
@@ -2879,7 +2830,7 @@ end
function SPAWN:SpawnFromVec3( Vec3, SpawnIndex )
--self:F( { self.SpawnTemplatePrefix, Vec3, SpawnIndex } )
local PointVec3 = POINT_VEC3:NewFromVec3( Vec3 )
local PointVec3 = COORDINATE:NewFromVec3( Vec3 )
--self:T2( PointVec3 )
if SpawnIndex then
@@ -2955,7 +2906,7 @@ end
-- Note that each point in the route assigned to the spawning group is reset to the point of the spawn.
-- You can use the returned group to further define the route to be followed.
-- @param #SPAWN self
-- @param Core.Point#POINT_VEC3 PointVec3 The PointVec3 coordinates where to spawn the group.
-- @param Core.Point#COORDINATE PointVec3 The COORDINATE coordinates where to spawn the group.
-- @param #number SpawnIndex (optional) The index which group to spawn within the given zone.
-- @return Wrapper.Group#GROUP that was spawned or #nil if nothing was spawned.
-- @usage
@@ -3003,12 +2954,12 @@ function SPAWN:SpawnFromVec2( Vec2, MinHeight, MaxHeight, SpawnIndex )
return self:SpawnFromVec3( { x = Vec2.x, y = Height, z = Vec2.y }, SpawnIndex ) -- y can be nil. In this case, spawn on the ground for vehicles, and in the template altitude for air.
end
--- Will spawn a group from a POINT_VEC2 in 3D space.
--- Will spawn a group from a COORDINATE in 3D space.
-- This method is mostly advisable to be used if you want to simulate spawning groups on the ground from air units, like vehicles.
-- Note that each point in the route assigned to the spawning group is reset to the point of the spawn.
-- You can use the returned group to further define the route to be followed.
-- @param #SPAWN self
-- @param Core.Point#POINT_VEC2 PointVec2 The PointVec2 coordinates where to spawn the group.
-- @param Core.Point#COORDINATE PointVec2 The coordinates where to spawn the group.
-- @param #number MinHeight (optional) The minimum height to spawn an airborne group into the zone.
-- @param #number MaxHeight (optional) The maximum height to spawn an airborne group into the zone.
-- @param #number SpawnIndex (optional) The index which group to spawn within the given zone.
@@ -3814,8 +3765,9 @@ end
--- Private method that randomizes the @{Core.Zone}s where the Group will be spawned.
-- @param #SPAWN self
-- @param #number SpawnIndex
-- @param #boolean RandomizePositionInZone If nil or true, also the position inside the selected random zone will be randomized. Set to false to use the center of the zone.
-- @return #SPAWN self
function SPAWN:_RandomizeZones( SpawnIndex )
function SPAWN:_RandomizeZones( SpawnIndex, RandomizePositionInZone)
--self:F( { self.SpawnTemplatePrefix, SpawnIndex, self.SpawnRandomizeZones } )
if self.SpawnRandomizeZones then
@@ -3829,7 +3781,11 @@ function SPAWN:_RandomizeZones( SpawnIndex )
--self:T2( "Preparing Spawn in Zone", SpawnZone:GetName() )
local SpawnVec2 = SpawnZone:GetRandomVec2()
local SpawnVec2 = SpawnZone:GetVec2()
if RandomizePositionInZone ~= false then
SpawnVec2 = SpawnZone:GetRandomVec2()
end
--self:T2( { SpawnVec2 = SpawnVec2 } )
@@ -3956,12 +3912,17 @@ end
-- @return #number count
function SPAWN:_CountAliveUnits()
local count = 0
--self:I("self.SpawnAliasPrefix="..tostring(self.SpawnAliasPrefix).." | self.SpawnTemplatePrefix="..tostring(self.SpawnTemplatePrefix))
if self.SpawnAliasPrefix then
if not self.SpawnAliasPrefixEscaped then self.SpawnAliasPrefixEscaped = string.gsub(self.SpawnAliasPrefix,"[%p%s]",".") end
--self:I("self.SpawnAliasPrefixEscaped="..tostring(self.SpawnAliasPrefixEscaped))
local SpawnAliasPrefix = self.SpawnAliasPrefixEscaped
local agroups = GROUP:FindAllByMatching(SpawnAliasPrefix)
for _,_grp in pairs(agroups) do
--self:I("Group Name = " .. _grp:GetName())
local game = self:_GetPrefixFromGroupName(_grp.GroupName)
--self:I("Game = "..game)
--self:I("Count = ".._grp:CountAliveUnits())
if game and game == self.SpawnAliasPrefix then
count = count + _grp:CountAliveUnits()
end
@@ -3977,15 +3938,16 @@ function SPAWN:_CountAliveUnits()
end
end
end
return count
self.AliveUnits = count
return self
end
---
-- @param #SPAWN self
-- @param Core.Event#EVENTDATA EventData
function SPAWN:_OnDeadOrCrash( EventData )
--self:T2( "Dead or crash event ID "..tostring(EventData.id or 0))
--self:T2( "Dead or crash event for "..tostring(EventData.IniUnitName or "none") )
--self:I( "Dead or crash event ID "..tostring(EventData.id or 0))
--self:I( "Dead or crash event for "..tostring(EventData.IniUnitName or "none") )
--if EventData.id == EVENTS.Dead then return end
@@ -3997,11 +3959,12 @@ function SPAWN:_OnDeadOrCrash( EventData )
local EventPrefix = self:_GetPrefixFromGroupName(unit.GroupName)
if EventPrefix then -- EventPrefix can be nil if no # is found, which means, no spawnable group!
--self:T2(string.format("EventPrefix = %s | SpawnAliasPrefix = %s | Old AliveUnits = %d",EventPrefix or "",self.SpawnAliasPrefix or "",self.AliveUnits or 0))
--self:I(string.format("EventPrefix = %s | SpawnAliasPrefix = %s | Old AliveUnits = %d",EventPrefix or "",self.SpawnAliasPrefix or "",self.AliveUnits or 0))
if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) and self.AliveUnits > 0 then
--self:I( { "Dead event: " .. EventPrefix } )
--self.AliveUnits = self.AliveUnits - 1
self.AliveUnits = self:_CountAliveUnits()
--self:I( { "Dead event: " .. EventPrefix } )
--self.AliveUnits = self.AliveUnits - 1
self:ScheduleOnce(1,self._CountAliveUnits,self)
--self.AliveUnits = self:_CountAliveUnits()
--self:I( "New Alive Units: " .. self.AliveUnits )
end
end
@@ -4049,7 +4012,7 @@ function SPAWN:_OnLand( EventData )
-- self:ReSpawn( SpawnGroupIndex )
-- Delay respawn by three seconds due to DCS 2.5.4.26368 OB bug https://github.com/FlightControl-Master/MOOSE/issues/1076
-- Bug was initially only for engine shutdown event but after ED "fixed" it, it now happens on landing events.
SCHEDULER:New( nil, self.ReSpawn, { self, SpawnGroupIndex }, 3 )
SCHEDULER:New( nil, self.ReSpawn, { self, SpawnGroupIndex }, self.RepeatOnLandingTime or 3 )
end
end
end

View File

@@ -105,7 +105,7 @@
--
-- * @{#SPAWNSTATIC.Spawn}(Heading, NewName) spawns the static with the set parameters. Optionally, heading and name can be given. The name **must be unique**!
-- * @{#SPAWNSTATIC.SpawnFromCoordinate}(Coordinate, Heading, NewName) spawn the static at the given coordinate. Optionally, heading and name can be given. The name **must be unique**!
-- * @{#SPAWNSTATIC.SpawnFromPointVec2}(PointVec2, Heading, NewName) spawns the static at a POINT_VEC2 coordinate. Optionally, heading and name can be given. The name **must be unique**!
-- * @{#SPAWNSTATIC.SpawnFromPointVec2}(PointVec2, Heading, NewName) spawns the static at a COORDINATE coordinate. Optionally, heading and name can be given. The name **must be unique**!
-- * @{#SPAWNSTATIC.SpawnFromZone}(Zone, Heading, NewName) spawns the static at the center of a @{Core.Zone}. Optionally, heading and name can be given. The name **must be unique**!
--
-- @field #SPAWNSTATIC SPAWNSTATIC
@@ -411,9 +411,9 @@ function SPAWNSTATIC:Spawn(Heading, NewName)
end
--- Creates a new @{Wrapper.Static} from a POINT_VEC2.
--- Creates a new @{Wrapper.Static} from a COORDINATE.
-- @param #SPAWNSTATIC self
-- @param Core.Point#POINT_VEC2 PointVec2 The 2D coordinate where to spawn the static.
-- @param Core.Point#COORDINATE PointVec2 The 2D coordinate where to spawn the static.
-- @param #number Heading The heading of the static, which is a number in degrees from 0 to 360.
-- @param #string NewName (Optional) The name of the new static.
-- @return Wrapper.Static#STATIC The static spawned.
@@ -535,12 +535,6 @@ function SPAWNSTATIC:_SpawnStatic(Template, CountryID)
-- Name of the spawned static.
Template.name = self.InitStaticName or string.format("%s#%05d", self.SpawnTemplatePrefix, self.SpawnIndex)
-- Add and register the new static.
local mystatic=_DATABASE:AddStatic(Template.name)
-- Debug output.
self:T(Template)
-- Add static to the game.
local Static=nil --DCS#StaticObject
@@ -577,12 +571,28 @@ function SPAWNSTATIC:_SpawnStatic(Template, CountryID)
self:T("Spawning Static")
self:T2({Template=Template})
Static=coalition.addStaticObject(CountryID, Template)
if Static then
self:T(string.format("Succesfully spawned static object \"%s\" ID=%d", Static:getName(), Static:getID()))
--[[
local static=StaticObject.getByName(Static:getName())
if static then
env.info(string.format("FF got static from StaticObject.getByName"))
else
env.error(string.format("FF error did NOT get static from StaticObject.getByName"))
end ]]
else
self:E(string.format("ERROR: DCS static object \"%s\" is nil!", tostring(Template.name)))
end
end
-- Add and register the new static.
local mystatic=_DATABASE:AddStatic(Template.name)
-- If there is a SpawnFunction hook defined, call it.
if self.SpawnFunctionHook then
-- delay calling this for .3 seconds so that it hopefully comes after the BIRTH event of the group.
self:ScheduleOnce(0.3,self.SpawnFunctionHook,mystatic, unpack(self.SpawnFunctionArguments))
self:ScheduleOnce(0.3, self.SpawnFunctionHook, mystatic, unpack(self.SpawnFunctionArguments))
end
return mystatic

View File

@@ -213,7 +213,7 @@ end
--- Returns if a PointVec3 is within the zone.
-- @param #ZONE_BASE self
-- @param Core.Point#POINT_VEC3 PointVec3 The PointVec3 to test.
-- @param Core.Point#COORDINATE PointVec3 The PointVec3 to test.
-- @return #boolean true if the PointVec3 is within the zone.
function ZONE_BASE:IsPointVec3InZone( PointVec3 )
local InZone = self:IsPointVec2InZone( PointVec3 )
@@ -227,16 +227,16 @@ function ZONE_BASE:GetVec2()
return nil
end
--- Returns a @{Core.Point#POINT_VEC2} of the zone.
--- Returns a @{Core.Point#COORDINATE} of the zone.
-- @param #ZONE_BASE self
-- @param DCS#Distance Height The height to add to the land height where the center of the zone is located.
-- @return Core.Point#POINT_VEC2 The PointVec2 of the zone.
-- @return Core.Point#COORDINATE The COORDINATE of the zone.
function ZONE_BASE:GetPointVec2()
--self:F2( self.ZoneName )
local Vec2 = self:GetVec2()
local PointVec2 = POINT_VEC2:NewFromVec2( Vec2 )
local PointVec2 = COORDINATE:NewFromVec2( Vec2 )
--self:T2( { PointVec2 } )
@@ -261,16 +261,16 @@ function ZONE_BASE:GetVec3( Height )
return Vec3
end
--- Returns a @{Core.Point#POINT_VEC3} of the zone.
--- Returns a @{Core.Point#COORDINATE} of the zone.
-- @param #ZONE_BASE self
-- @param DCS#Distance Height The height to add to the land height where the center of the zone is located.
-- @return Core.Point#POINT_VEC3 The PointVec3 of the zone.
-- @return Core.Point#COORDINATE The PointVec3 of the zone.
function ZONE_BASE:GetPointVec3( Height )
--self:F2( self.ZoneName )
local Vec3 = self:GetVec3( Height )
local PointVec3 = POINT_VEC3:NewFromVec3( Vec3 )
local PointVec3 = COORDINATE:NewFromVec3( Vec3 )
--self:T2( { PointVec3 } )
@@ -330,16 +330,16 @@ function ZONE_BASE:GetRandomVec2()
return nil
end
--- Define a random @{Core.Point#POINT_VEC2} within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec2 table.
--- Define a random @{Core.Point#COORDINATE} within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec2 table.
-- @param #ZONE_BASE self
-- @return Core.Point#POINT_VEC2 The PointVec2 coordinates.
-- @return Core.Point#COORDINATE The COORDINATE coordinates.
function ZONE_BASE:GetRandomPointVec2()
return nil
end
--- Define a random @{Core.Point#POINT_VEC3} within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec3 table.
--- Define a random @{Core.Point#COORDINATE} within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec3 table.
-- @param #ZONE_BASE self
-- @return Core.Point#POINT_VEC3 The PointVec3 coordinates.
-- @return Core.Point#COORDINATE The COORDINATE coordinates.
function ZONE_BASE:GetRandomPointVec3()
return nil
end
@@ -605,10 +605,13 @@ function ZONE_BASE:Trigger(Objects)
self:AddTransition("TriggerStopped","TriggerStart","TriggerRunning")
self:AddTransition("*","EnteredZone","*")
self:AddTransition("*","LeftZone","*")
self:AddTransition("*","ZoneEmpty","*")
self:AddTransition("*","ObjectDead","*")
self:AddTransition("*","TriggerRunCheck","*")
self:AddTransition("*","TriggerStop","TriggerStopped")
self:TriggerStart()
self.checkobjects = Objects
self.ObjectsInZone = false
if UTILS.IsInstanceOf(Objects,"SET_BASE") then
self.objectset = Objects.Set
else
@@ -646,6 +649,22 @@ function ZONE_BASE:Trigger(Objects)
-- @param #string Event Event.
-- @param #string To To state.
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The controllable leaving the zone.
--- On After "ObjectDead" event. An observed object has left the zone.
-- @function [parent=#ZONE_BASE] OnAfterObjectDead
-- @param #ZONE_BASE self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The controllable which died. Might be nil.
--- On After "ZoneEmpty" event. All observed objects have left the zone or are dead.
-- @function [parent=#ZONE_BASE] OnAfterZoneEmpty
-- @param #ZONE_BASE self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
end
--- (Internal) Check the assigned objects for being in/out of the zone
@@ -659,9 +678,13 @@ function ZONE_BASE:_TriggerCheck(fromstart)
-- just earmark everyone in/out
for _,_object in pairs(objectset) do
local obj = _object -- Wrapper.Controllable#CONTROLLABLE
if not obj.TriggerInZone then obj.TriggerInZone = {} end
if not obj.TriggerInZone then
obj.TriggerInZone = {}
obj.TriggerZoneDeadNotification = false
end
if obj and obj:IsAlive() and self:IsCoordinateInZone(obj:GetCoordinate()) then
obj.TriggerInZone[self.ZoneName] = true
self.ObjectsInZone = true
else
obj.TriggerInZone[self.ZoneName] = false
end
@@ -669,6 +692,7 @@ function ZONE_BASE:_TriggerCheck(fromstart)
end
else
-- Check for changes
local objcount = 0
for _,_object in pairs(objectset) do
local obj = _object -- Wrapper.Controllable#CONTROLLABLE
if obj and obj:IsAlive() then
@@ -683,11 +707,20 @@ function ZONE_BASE:_TriggerCheck(fromstart)
-- is obj in zone?
local inzone = self:IsCoordinateInZone(obj:GetCoordinate())
--self:I("Object "..obj:GetName().." is in zone: "..tostring(inzone))
if inzone and obj.TriggerInZone[self.ZoneName] then
-- just count
objcount = objcount + 1
self.ObjectsInZone = true
obj.TriggerZoneDeadNotification = false
end
if inzone and not obj.TriggerInZone[self.ZoneName] then
-- wasn't in zone before
--self:I("Newly entered")
self:__EnteredZone(0.5,obj)
obj.TriggerInZone[self.ZoneName] = true
objcount = objcount + 1
self.ObjectsInZone = true
obj.TriggerZoneDeadNotification = false
elseif (not inzone) and obj.TriggerInZone[self.ZoneName] then
-- has left the zone
--self:I("Newly left")
@@ -696,8 +729,21 @@ function ZONE_BASE:_TriggerCheck(fromstart)
else
--self:I("Not left or not entered, or something went wrong!")
end
else
-- object dead
if not obj.TriggerZoneDeadNotification == true then
obj.TriggerInZone = nil
self:__ObjectDead(0.5,obj)
obj.TriggerZoneDeadNotification = true
end
end
end
-- zone empty?
if objcount == 0 and self.ObjectsInZone == true then
-- zone was not but is now empty
self.ObjectsInZone = false
self:__ZoneEmpty(0.5)
end
end
return self
end
@@ -768,8 +814,8 @@ end
-- Various functions exist to find random points within the zone.
--
-- * @{#ZONE_RADIUS.GetRandomVec2}(): Gets a random 2D point in the zone.
-- * @{#ZONE_RADIUS.GetRandomPointVec2}(): Gets a @{Core.Point#POINT_VEC2} object representing a random 2D point in the zone.
-- * @{#ZONE_RADIUS.GetRandomPointVec3}(): Gets a @{Core.Point#POINT_VEC3} object representing a random 3D point in the zone. Note that the height of the point is at landheight.
-- * @{#ZONE_RADIUS.GetRandomPointVec2}(): Gets a @{Core.Point#COORDINATE} object representing a random 2D point in the zone.
-- * @{#ZONE_RADIUS.GetRandomPointVec3}(): Gets a @{Core.Point#COORDINATE} object representing a random 3D point in the zone. Note that the height of the point is at landheight.
--
-- ## Draw zone
--
@@ -964,7 +1010,7 @@ function ZONE_RADIUS:SmokeZone( SmokeColor, Points, AddHeight, AngleOffset )
local Radial = ( Angle + AngleOffset ) * RadialBase / 360
Point.x = Vec2.x + math.cos( Radial ) * self:GetRadius()
Point.y = Vec2.y + math.sin( Radial ) * self:GetRadius()
POINT_VEC2:New( Point.x, Point.y, AddHeight ):Smoke( SmokeColor )
COORDINATE:New( Point.x, AddHeight, Point.y ):Smoke( SmokeColor )
end
return self
@@ -994,7 +1040,7 @@ function ZONE_RADIUS:FlareZone( FlareColor, Points, Azimuth, AddHeight )
local Radial = Angle * RadialBase / 360
Point.x = Vec2.x + math.cos( Radial ) * self:GetRadius()
Point.y = Vec2.y + math.sin( Radial ) * self:GetRadius()
POINT_VEC2:New( Point.x, Point.y, AddHeight ):Flare( FlareColor, Azimuth )
COORDINATE:New( Point.x, AddHeight, Point.y ):Flare( FlareColor, Azimuth )
end
return self
@@ -1415,7 +1461,7 @@ function ZONE_RADIUS:SearchZone( EvaluateFunction, ObjectCategories )
id = world.VolumeType.SPHERE,
params = {
point = ZoneCoord:GetVec3(),
radius = ZoneRadius / 2,
radius = ZoneRadius,
}
}
@@ -1515,15 +1561,15 @@ function ZONE_RADIUS:GetRandomVec2(inner, outer, surfacetypes)
return point
end
--- Returns a @{Core.Point#POINT_VEC2} object reflecting a random 2D location within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec2 table.
--- Returns a @{Core.Point#COORDINATE} object reflecting a random 2D location within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec2 table.
-- @param #ZONE_RADIUS self
-- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0.
-- @param #number outer (optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone.
-- @return Core.Point#POINT_VEC2 The @{Core.Point#POINT_VEC2} object reflecting the random 3D location within the zone.
-- @return Core.Point#COORDINATE The @{Core.Point#COORDINATE} object reflecting the random 3D location within the zone.
function ZONE_RADIUS:GetRandomPointVec2( inner, outer )
--self:F( self.ZoneName, inner, outer )
local PointVec2 = POINT_VEC2:NewFromVec2( self:GetRandomVec2( inner, outer ) )
local PointVec2 = COORDINATE:NewFromVec2( self:GetRandomVec2( inner, outer ) )
--self:T3( { PointVec2 } )
@@ -1546,15 +1592,15 @@ function ZONE_RADIUS:GetRandomVec3( inner, outer )
end
--- Returns a @{Core.Point#POINT_VEC3} object reflecting a random 3D location within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec3 table.
--- Returns a @{Core.Point#COORDINATE} object reflecting a random 3D location within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec3 table.
-- @param #ZONE_RADIUS self
-- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0.
-- @param #number outer (optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone.
-- @return Core.Point#POINT_VEC3 The @{Core.Point#POINT_VEC3} object reflecting the random 3D location within the zone.
-- @return Core.Point#COORDINATE The @{Core.Point#COORDINATE} object reflecting the random 3D location within the zone.
function ZONE_RADIUS:GetRandomPointVec3( inner, outer )
--self:F( self.ZoneName, inner, outer )
local PointVec3 = POINT_VEC3:NewFromVec2( self:GetRandomVec2( inner, outer ) )
local PointVec3 = COORDINATE:NewFromVec2( self:GetRandomVec2( inner, outer ) )
--self:T3( { PointVec3 } )
@@ -1990,15 +2036,15 @@ function ZONE_GROUP:GetRandomVec2()
return Point
end
--- Returns a @{Core.Point#POINT_VEC2} object reflecting a random 2D location within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec2 table.
--- Returns a @{Core.Point#COORDINATE} object reflecting a random 2D location within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec2 table.
-- @param #ZONE_GROUP self
-- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0.
-- @param #number outer (optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone.
-- @return Core.Point#POINT_VEC2 The @{Core.Point#POINT_VEC2} object reflecting the random 3D location within the zone.
-- @return Core.Point#COORDINATE The @{Core.Point#COORDINATE} object reflecting the random 3D location within the zone.
function ZONE_GROUP:GetRandomPointVec2( inner, outer )
--self:F( self.ZoneName, inner, outer )
local PointVec2 = POINT_VEC2:NewFromVec2( self:GetRandomVec2() )
local PointVec2 = COORDINATE:NewFromVec2( self:GetRandomVec2() )
--self:T3( { PointVec2 } )
@@ -2046,7 +2092,7 @@ function _ZONE_TRIANGLE:New(p1, p2, p3)
end
self.SurfaceArea = math.abs((p2.x - p1.x) * (p3.y - p1.y) - (p3.x - p1.x) * (p2.y - p1.y)) * 0.5
return self
end
@@ -2054,7 +2100,7 @@ end
-- @param #_ZONE_TRIANGLE self
-- @param #table pt The point to check
-- @param #table points (optional) The points of the triangle, or 3 other points if you're just using the TRIANGLE class without an object of it
-- @return #bool True if the point is contained, false otherwise
-- @return #boolean True if the point is contained, false otherwise
function _ZONE_TRIANGLE:ContainsPoint(pt, points)
points = points or self.Points
@@ -2146,8 +2192,8 @@ end
-- Various functions exist to find random points within the zone.
--
-- * @{#ZONE_POLYGON_BASE.GetRandomVec2}(): Gets a random 2D point in the zone.
-- * @{#ZONE_POLYGON_BASE.GetRandomPointVec2}(): Return a @{Core.Point#POINT_VEC2} object representing a random 2D point within the zone.
-- * @{#ZONE_POLYGON_BASE.GetRandomPointVec3}(): Return a @{Core.Point#POINT_VEC3} object representing a random 3D point at landheight within the zone.
-- * @{#ZONE_POLYGON_BASE.GetRandomPointVec2}(): Return a @{Core.Point#COORDINATE} object representing a random 2D point within the zone.
-- * @{#ZONE_POLYGON_BASE.GetRandomPointVec3}(): Return a @{Core.Point#COORDINATE} object representing a random 3D point at landheight within the zone.
--
-- ## Draw zone
--
@@ -2563,7 +2609,7 @@ function ZONE_POLYGON_BASE:ReFill(Color,Alpha)
self.FillTriangles = {}
end
-- refill
for _, triangle in pairs(self._Triangles) do
for _,triangle in pairs(self._Triangles) do
local draw_ids = triangle:Fill(coalition,color,alpha,nil)
self.FillTriangles = draw_ids
table.combine(self.DrawID, draw_ids)
@@ -2723,7 +2769,7 @@ function ZONE_POLYGON_BASE:SmokeZone( SmokeColor, Segments )
for Segment = 0, Segments do -- We divide each line in 5 segments and smoke a point on the line.
local PointX = self._.Polygon[i].x + ( Segment * DeltaX / Segments )
local PointY = self._.Polygon[i].y + ( Segment * DeltaY / Segments )
POINT_VEC2:New( PointX, PointY ):Smoke( SmokeColor )
COORDINATE:New( PointX, 0, PointY ):Smoke( SmokeColor )
end
j = i
i = i + 1
@@ -2758,7 +2804,7 @@ function ZONE_POLYGON_BASE:FlareZone( FlareColor, Segments, Azimuth, AddHeight )
for Segment = 0, Segments do -- We divide each line in 5 segments and smoke a point on the line.
local PointX = self._.Polygon[i].x + ( Segment * DeltaX / Segments )
local PointY = self._.Polygon[i].y + ( Segment * DeltaY / Segments )
POINT_VEC2:New( PointX, PointY, AddHeight ):Flare(FlareColor, Azimuth)
COORDINATE:New( PointX, AddHeight, PointY ):Flare(FlareColor, Azimuth)
end
j = i
i = i + 1
@@ -2834,26 +2880,26 @@ function ZONE_POLYGON_BASE:GetRandomVec2()
end
end
--- Return a @{Core.Point#POINT_VEC2} object representing a random 2D point at landheight within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec2 table.
--- Return a @{Core.Point#COORDINATE} object representing a random 2D point at landheight within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec2 table.
-- @param #ZONE_POLYGON_BASE self
-- @return @{Core.Point#POINT_VEC2}
-- @return @{Core.Point#COORDINATE}
function ZONE_POLYGON_BASE:GetRandomPointVec2()
--self:F2()
local PointVec2 = POINT_VEC2:NewFromVec2( self:GetRandomVec2() )
local PointVec2 = COORDINATE:NewFromVec2( self:GetRandomVec2() )
--self:T2( PointVec2 )
return PointVec2
end
--- Return a @{Core.Point#POINT_VEC3} object representing a random 3D point at landheight within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec3 table.
--- Return a @{Core.Point#COORDINATE} object representing a random 3D point at landheight within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec3 table.
-- @param #ZONE_POLYGON_BASE self
-- @return @{Core.Point#POINT_VEC3}
-- @return @{Core.Point#COORDINATE}
function ZONE_POLYGON_BASE:GetRandomPointVec3()
--self:F2()
local PointVec3 = POINT_VEC3:NewFromVec2( self:GetRandomVec2() )
local PointVec3 = COORDINATE:NewFromVec2( self:GetRandomVec2() )
--self:T2( PointVec3 )
@@ -3536,7 +3582,37 @@ do -- ZONE_ELASTIC
return self
end
--- Remove a vertex (point) from the polygon.
-- @param #ZONE_ELASTIC self
-- @param DCS#Vec2 Vec2 Point in 2D (with x and y coordinates).
-- @return #ZONE_ELASTIC self
function ZONE_ELASTIC:RemoveVertex2D(Vec2)
local found = false
local findex = 0
for _id,_vec2 in pairs(self.points) do
if _vec2.x == Vec2.x and _vec2.y == Vec2.y then
found = true
findex = _id
break
end
end
if found == true and findex > 0 then
table.remove(self.points,findex)
end
return self
end
--- Remove a vertex (point) from the polygon.
-- @param #ZONE_ELASTIC self
-- @param DCS#Vec3 Vec3 Point in 3D (with x, y and z coordinates). Only the x and z coordinates are used.
-- @return #ZONE_ELASTIC self
function ZONE_ELASTIC:RemoveVertex3D(Vec3)
return self:RemoveVertex2D({x=Vec3.x, y=Vec3.z})
end
--- Add a vertex (point) to the polygon.
-- @param #ZONE_ELASTIC self
@@ -3574,7 +3650,7 @@ do -- ZONE_ELASTIC
-- Debug info.
--self:T(string.format("Updating ZONE_ELASTIC %s", tostring(self.ZoneName)))
-- Copy all points.
local points=UTILS.DeepCopy(self.points or {})
@@ -3592,6 +3668,9 @@ do -- ZONE_ELASTIC
-- Update polygon verticies from points.
self._.Polygon=self:_ConvexHull(points)
self._Triangles = self:_Triangulate()
self.SurfaceArea = self:_CalculateSurfaceArea()
if Draw~=false then
if self.DrawID or Draw==true then
@@ -3790,7 +3869,7 @@ end
--- Checks if a point is contained within the oval.
-- @param #ZONE_OVAL self
-- @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 ZONE_OVAL:IsVec2InZone(vec2)
local cos, sin = math.cos, math.sin
local dx = vec2.x - self.CenterVec2.x
@@ -3852,18 +3931,18 @@ function ZONE_OVAL:GetRandomVec2()
return {x=rx, y=ry}
end
--- Define a random @{Core.Point#POINT_VEC2} within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec2 table.
--- Define a random @{Core.Point#COORDINATE} within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec2 table.
-- @param #ZONE_OVAL self
-- @return Core.Point#POINT_VEC2 The PointVec2 coordinates.
-- @return Core.Point#COORDINATE The COORDINATE coordinates.
function ZONE_OVAL:GetRandomPointVec2()
return POINT_VEC2:NewFromVec2(self:GetRandomVec2())
return COORDINATE:NewFromVec2(self:GetRandomVec2())
end
--- Define a random @{Core.Point#POINT_VEC2} within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec3 table.
--- Define a random @{Core.Point#COORDINATE} within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec3 table.
-- @param #ZONE_OVAL self
-- @return Core.Point#POINT_VEC2 The PointVec2 coordinates.
-- @return Core.Point#COORDINATE The COORDINATE coordinates.
function ZONE_OVAL:GetRandomPointVec3()
return POINT_VEC3:NewFromVec3(self:GetRandomVec2())
return COORDINATE:NewFromVec3(self:GetRandomVec2())
end
--- Draw the zone on the F10 map.
@@ -4003,15 +4082,15 @@ do -- ZONE_AIRBASE
return ZoneVec2
end
--- Returns a @{Core.Point#POINT_VEC2} object reflecting a random 2D location within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec2 table.
--- Returns a @{Core.Point#COORDINATE} object reflecting a random 2D location within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec2 table.
-- @param #ZONE_AIRBASE self
-- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0.
-- @param #number outer (optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone.
-- @return Core.Point#POINT_VEC2 The @{Core.Point#POINT_VEC2} object reflecting the random 3D location within the zone.
-- @return Core.Point#COORDINATE The @{Core.Point#COORDINATE} object reflecting the random 3D location within the zone.
function ZONE_AIRBASE:GetRandomPointVec2( inner, outer )
--self:F( self.ZoneName, inner, outer )
local PointVec2 = POINT_VEC2:NewFromVec2( self:GetRandomVec2() )
local PointVec2 = COORDINATE:NewFromVec2( self:GetRandomVec2() )
--self:T3( { PointVec2 } )

View File

@@ -107,7 +107,7 @@ function ZONE_DETECTION:SmokeZone( SmokeColor, Points, AddHeight, AngleOffset )
local Radial = ( Angle + AngleOffset ) * RadialBase / 360
Point.x = Vec2.x + math.cos( Radial ) * self:GetRadius()
Point.y = Vec2.y + math.sin( Radial ) * self:GetRadius()
POINT_VEC2:New( Point.x, Point.y, AddHeight ):Smoke( SmokeColor )
COORDINATE:New( Point.x, AddHeight, Point.y):Smoke( SmokeColor )
end
return self
@@ -138,7 +138,7 @@ function ZONE_DETECTION:FlareZone( FlareColor, Points, Azimuth, AddHeight )
local Radial = Angle * RadialBase / 360
Point.x = Vec2.x + math.cos( Radial ) * self:GetRadius()
Point.y = Vec2.y + math.sin( Radial ) * self:GetRadius()
POINT_VEC2:New( Point.x, Point.y, AddHeight ):Flare( FlareColor, Azimuth )
COORDINATE:New( Point.x, AddHeight, Point.y ):Flare( FlareColor, Azimuth )
end
return self
@@ -202,4 +202,3 @@ function ZONE_DETECTION:IsVec3InZone( Vec3 )
return InZone
end

View File

@@ -14,6 +14,7 @@ do -- world
-- @field #world.event event [https://wiki.hoggitworld.com/view/DCS_enum_world](https://wiki.hoggitworld.com/view/DCS_enum_world)
-- @field #world.BirthPlace BirthPlace The birthplace enumerator is used to define where an aircraft or helicopter has spawned in association with birth events.
-- @field #world.VolumeType VolumeType The volumeType enumerator defines the types of 3d geometery used within the [world.searchObjects](https://wiki.hoggitworld.com/view/DCS_func_searchObjects) function.
-- @field #world.weather weather Weather functions for fog etc.
--- The world singleton contains functions centered around two different but extremely useful functions.
-- * Events and event handlers are all governed within world.
@@ -132,6 +133,36 @@ do -- world
-- @function [parent=#world] getAirbases
-- @param #number coalitionId The coalition side number ID. Default is all airbases are returned.
-- @return #table Table of DCS airbase objects.
--- Weather functions.
-- @type world.weather
--- Fog animation data structure.
-- @type world.FogAnimation
-- @field #number time
-- @field #number visibility
-- @field #number thickness
--- Returns the current fog thickness.
-- @function [parent=#world.weather] getFogThickness Returns the fog thickness.
-- @return #number Fog thickness in meters. If there is no fog, zero is returned.
--- Sets the fog thickness instantly. Any current fog animation is discarded.
-- @function [parent=#world.weather] setFogThickness
-- @param #number thickness Fog thickness in meters. Set to zero to disable fog.
--- Returns the current fog visibility distance.
-- @function [parent=#world.weather] getFogVisibilityDistance Returns the current maximum visibility distance in meters. Returns zero if fog is not present.
--- Instantly sets the maximum visibility distance of fog at sea level when looking at the horizon. Any current fog animation is discarded. Set zero to disable the fog.
-- @function [parent=#world.weather] setFogVisibilityDistance
-- @param #number visibility Max fog visibility in meters. Set to zero to disable fog.
--- Sets fog animation keys. Time is set in seconds and relative to the current simulation time, where time=0 is the current moment.
-- Time must be increasing. Previous animation is always discarded despite the data being correct.
-- @function [parent=#world.weather] setFogAnimation
-- @param #world.FogAnimation animation List of fog animations
end -- world
@@ -167,7 +198,7 @@ end -- env
do -- radio
---@type radio
--@type radio
-- @field #radio.modulation modulation
---
@@ -407,7 +438,7 @@ do -- coalition
-- @param #table groupData Group data table.
-- @return DCS#Group The spawned Group object.
--- Dynamically spawns a static object. See [hoggit](https://wiki.hoggitworld.com/view/DCS_func_addGroup)
--- Dynamically spawns a static object. See [hoggit](https://wiki.hoggitworld.com/view/DCS_func_addStaticObject)
-- @function [parent=#coalition] addStaticObject
-- @param #number countryId Id of the country.
-- @param #table groupData Group data table.
@@ -420,6 +451,7 @@ end -- coalition
do -- Types
--- Descriptors.
-- @type Desc
-- @field #number speedMax0 Max speed in meters/second at zero altitude.
-- @field #number massEmpty Empty mass in kg.
@@ -598,9 +630,13 @@ do -- Object
--- @function [parent=#Object] destroy
-- @param #Object self
--- @function [parent=#Object] getCategory
--- Returns an enumerator of the category for the specific object.
-- The enumerator returned is dependent on the category of the object and how the function is called.
-- As of DCS 2.9.2 when this function is called on an Object, Unit, Weapon, or Airbase a 2nd value will be returned which details the object sub-category value.
-- @function [parent=#Object] getCategory
-- @param #Object self
-- @return #Object.Category
-- @return #Object.Category The object category (1=UNIT, 2=WEAPON, 3=STATIC, 4=BASE, 5=SCENERY, 6=Cargo)
-- @return #number The subcategory of the passed object, e.g. Unit.Category if a unit object was passed.
--- Returns type name of the Object.
-- @function [parent=#Object] getTypeName
@@ -1013,14 +1049,16 @@ do -- Spot
end -- Spot
do -- Controller
--- Controller is an object that performs A.I.-tasks. Other words controller is an instance of A.I.. Controller stores current main task, active enroute tasks and behavior options. Controller performs commands. Please, read DCS A-10C GUI Manual EN.pdf chapter "Task Planning for Unit Groups", page 91 to understand A.I. system of DCS:A-10C.
--
-- This class has 2 types of functions:
--
-- * Tasks
-- * Commands: Commands are instant actions those required zero time to perform. Commands may be used both for control unit/group behavior and control game mechanics.
-- * Commands: Commands are instant actions those required zero time to perform. Commands may be used both for control unit/group behavior and control game mechanics.
--
-- @type Controller
-- @field #Controller.Detection Detection Enum contains identifiers of surface types.
-- @field #Controller.Detection Detection Enum contains identifiers of surface types.
--- Enables and disables the controller.
-- Note: Now it works only for ground / naval groups!
@@ -1079,18 +1117,18 @@ do -- Controller
-- Detection
--- Enum contains identifiers of surface types.
--- Enum containing detection types.
-- @type Controller.Detection
-- @field VISUAL
-- @field OPTIC
-- @field RADAR
-- @field IRST
-- @field RWR
-- @field DLINK
-- @field #number VISUAL Visual detection. Numeric value 1.
-- @field #number OPTIC Optical detection. Numeric value 2.
-- @field #number RADAR Radar detection. Numeric value 4.
-- @field #number IRST Infra-red search and track detection. Numeric value 8.
-- @field #number RWR Radar Warning Receiver detection. Numeric value 16.
-- @field #number DLINK Data link detection. Numeric value 32.
--- Detected target.
-- @type DetectedTarget
-- @field Wrapper.Object#Object object The target
-- @type Controller.DetectedTarget
-- @field DCS#Object object The target
-- @field #boolean visible The target is visible
-- @field #boolean type The target type is known
-- @field #boolean distance Distance to the target is known
@@ -1103,9 +1141,9 @@ do -- Controller
-- @param #Controller.Detection detection Controller.Detection detection1, Controller.Detection detection2, ... Controller.Detection detectionN
-- @return #boolean detected True if the target is detected.
-- @return #boolean visible Has effect only if detected is true. True if the target is visible now.
-- @return #boolean type Has effect only if detected is true. True if the target type is known.
-- @return #boolean distance Has effect only if detected is true. True if the distance to the target is known.
-- @return #ModelTime lastTime Has effect only if visible is false. Last time when target was seen.
-- @return #boolean type Has effect only if detected is true. True if the target type is known.
-- @return #boolean distance Has effect only if detected is true. True if the distance to the target is known.
-- @return #Vec3 lastPos Has effect only if visible is false. Last position of the target when it was seen.
-- @return #Vec3 lastVel Has effect only if visible is false. Last velocity of the target when it was seen.
@@ -1131,6 +1169,7 @@ end -- Controller
do -- Unit
--- Unit.
-- @type Unit
-- @extends #CoalitionObject
-- @field ID Identifier of an unit. It assigned to an unit by the Mission Editor automatically.

View File

@@ -18,7 +18,7 @@
-- ### Author: FlightControl - Framework Design & Programming
-- ### Refactoring to use the Runway auto-detection: Applevangelist
-- @date August 2022
-- Last Update Nov 2023
-- Last Update Feb 2025
--
-- ===
--
@@ -416,7 +416,7 @@ end
-- @field #ATC_GROUND_UNIVERSAL
ATC_GROUND_UNIVERSAL = {
ClassName = "ATC_GROUND_UNIVERSAL",
Version = "0.0.1",
Version = "0.0.2",
SetClient = nil,
Airbases = nil,
AirbaseList = nil,
@@ -441,17 +441,25 @@ function ATC_GROUND_UNIVERSAL:New(AirbaseList)
self:T( { self.ClassName } )
self.Airbases = {}
for _name,_ in pairs(_DATABASE.AIRBASES) do
self.Airbases[_name]={}
end
self.AirbaseList = AirbaseList
if not self.AirbaseList then
self.AirbaseList = {}
for _name,_ in pairs(_DATABASE.AIRBASES) do
self.AirbaseList[_name]=_name
for _name,_base in pairs(_DATABASE.AIRBASES) do
-- DONE exclude FARPS and Ships
if _base and _base.isAirdrome == true then
self.AirbaseList[_name]=_name
self.Airbases[_name]={}
end
end
else
for _,_name in pairs(AirbaseList) do
-- DONE exclude FARPS and Ships
local airbase = _DATABASE:FindAirbase(_name)
if airbase and (airbase.isAirdrome == true) then
self.Airbases[_name]={}
end
end
end
@@ -721,14 +729,18 @@ function ATC_GROUND_UNIVERSAL:_AirbaseMonitor()
if NotInRunwayZone then
local Taxi = Client:GetState( self, "Taxi" )
if IsOnGround then
local Taxi = Client:GetState( self, "Taxi" )
self:T( Taxi )
if Taxi == false then
local Velocity = VELOCITY:New( AirbaseMeta.KickSpeed or self.KickSpeed )
Client:Message( "Welcome to " .. AirbaseID .. ". The maximum taxiing speed is " ..
Velocity:ToString() , 20, "ATC" )
Client:SetState( self, "Taxi", true )
Client:SetState( self, "Speeding", false )
Client:SetState( self, "Warnings", 0 )
end
-- TODO: GetVelocityKMH function usage
@@ -737,7 +749,7 @@ function ATC_GROUND_UNIVERSAL:_AirbaseMonitor()
local IsAboveRunway = Client:IsAboveRunway()
self:T( {IsAboveRunway, IsOnGround, Velocity:Get() })
if IsOnGround then
if IsOnGround and not Taxi then
local Speeding = false
if AirbaseMeta.MaximumKickSpeed then
if Velocity:Get() > AirbaseMeta.MaximumKickSpeed then
@@ -749,15 +761,17 @@ function ATC_GROUND_UNIVERSAL:_AirbaseMonitor()
end
end
if Speeding == true then
MESSAGE:New( "Penalty! Player " .. Client:GetPlayerName() ..
" has been kicked, due to a severe airbase traffic rule violation ...", 10, "ATC" ):ToAll()
Client:Destroy()
Client:SetState( self, "Speeding", false )
Client:SetState( self, "Warnings", 0 )
--MESSAGE:New( "Penalty! Player " .. Client:GetPlayerName() ..
-- " has been kicked, due to a severe airbase traffic rule violation ...", 10, "ATC" ):ToAll()
--Client:Destroy()
Client:SetState( self, "Speeding", true )
local SpeedingWarnings = Client:GetState( self, "Warnings" )
Client:SetState( self, "Warnings", SpeedingWarnings + 1 )
Client:Message( "Warning " .. SpeedingWarnings .. "/3! Airbase traffic rule violation! Slow down now! Your speed is " ..
Velocity:ToString(), 5, "ATC" )
end
end
if IsOnGround then
local Speeding = false
@@ -1035,23 +1049,23 @@ end
-- The following airbases are monitored at the Nevada region.
-- Use the @{Wrapper.Airbase#AIRBASE.Nevada} enumeration to select the airbases to be monitored.
--
-- * `AIRBASE.Nevada.Beatty_Airport`
-- * `AIRBASE.Nevada.Boulder_City_Airport`
-- * `AIRBASE.Nevada.Creech_AFB`
-- * `AIRBASE.Nevada.Beatty`
-- * `AIRBASE.Nevada.Boulder_City`
-- * `AIRBASE.Nevada.Creech`
-- * `AIRBASE.Nevada.Echo_Bay`
-- * `AIRBASE.Nevada.Groom_Lake_AFB`
-- * `AIRBASE.Nevada.Henderson_Executive_Airport`
-- * `AIRBASE.Nevada.Jean_Airport`
-- * `AIRBASE.Nevada.Laughlin_Airport`
-- * `AIRBASE.Nevada.Groom_Lake`
-- * `AIRBASE.Nevada.Henderson_Executive`
-- * `AIRBASE.Nevada.Jean`
-- * `AIRBASE.Nevada.Laughlin`
-- * `AIRBASE.Nevada.Lincoln_County`
-- * `AIRBASE.Nevada.McCarran_International_Airport`
-- * `AIRBASE.Nevada.McCarran_International`
-- * `AIRBASE.Nevada.Mesquite`
-- * `AIRBASE.Nevada.Mina_Airport`
-- * `AIRBASE.Nevada.Nellis_AFB`
-- * `AIRBASE.Nevada.Mina`
-- * `AIRBASE.Nevada.Nellis`
-- * `AIRBASE.Nevada.North_Las_Vegas`
-- * `AIRBASE.Nevada.Pahute_Mesa_Airstrip`
-- * `AIRBASE.Nevada.Tonopah_Airport`
-- * `AIRBASE.Nevada.Tonopah_Test_Range_Airfield`
-- * `AIRBASE.Nevada.Pahute_Mesa`
-- * `AIRBASE.Nevada.Tonopah`
-- * `AIRBASE.Nevada.Tonopah_Test_Range`
--
-- # Installation
--
@@ -1088,10 +1102,10 @@ end
--
-- -- Monitor specific airbases.
-- ATC_Ground = ATC_GROUND_NEVADA:New(
-- { AIRBASE.Nevada.Laughlin_Airport,
-- { AIRBASE.Nevada.Laughlin,
-- AIRBASE.Nevada.Lincoln_County,
-- AIRBASE.Nevada.North_Las_Vegas,
-- AIRBASE.Nevada.McCarran_International_Airport
-- AIRBASE.Nevada.McCarran_International
-- }
-- )
--
@@ -1330,33 +1344,33 @@ end
-- The following airbases are monitored at the PersianGulf region.
-- Use the @{Wrapper.Airbase#AIRBASE.PersianGulf} enumeration to select the airbases to be monitored.
--
-- * `AIRBASE.PersianGulf.Abu_Musa_Island_Airport`
-- * `AIRBASE.PersianGulf.Al_Dhafra_AB`
-- * `AIRBASE.PersianGulf.Abu_Musa_Island`
-- * `AIRBASE.PersianGulf.Al_Dhafra_AFB`
-- * `AIRBASE.PersianGulf.Al_Maktoum_Intl`
-- * `AIRBASE.PersianGulf.Al_Minhad_AB`
-- * `AIRBASE.PersianGulf.Al_Minhad_AFB`
-- * `AIRBASE.PersianGulf.Bandar_Abbas_Intl`
-- * `AIRBASE.PersianGulf.Bandar_Lengeh`
-- * `AIRBASE.PersianGulf.Dubai_Intl`
-- * `AIRBASE.PersianGulf.Fujairah_Intl`
-- * `AIRBASE.PersianGulf.Havadarya`
-- * `AIRBASE.PersianGulf.Kerman_Airport`
-- * `AIRBASE.PersianGulf.Kerman`
-- * `AIRBASE.PersianGulf.Khasab`
-- * `AIRBASE.PersianGulf.Lar_Airbase`
-- * `AIRBASE.PersianGulf.Lar`
-- * `AIRBASE.PersianGulf.Qeshm_Island`
-- * `AIRBASE.PersianGulf.Sharjah_Intl`
-- * `AIRBASE.PersianGulf.Shiraz_International_Airport`
-- * `AIRBASE.PersianGulf.Shiraz_Intl`
-- * `AIRBASE.PersianGulf.Sir_Abu_Nuayr`
-- * `AIRBASE.PersianGulf.Sirri_Island`
-- * `AIRBASE.PersianGulf.Tunb_Island_AFB`
-- * `AIRBASE.PersianGulf.Tunb_Kochak`
-- * `AIRBASE.PersianGulf.Sas_Al_Nakheel_Airport`
-- * `AIRBASE.PersianGulf.Bandar_e_Jask_airfield`
-- * `AIRBASE.PersianGulf.Abu_Dhabi_International_Airport`
-- * `AIRBASE.PersianGulf.Al_Bateen_Airport`
-- * `AIRBASE.PersianGulf.Kish_International_Airport`
-- * `AIRBASE.PersianGulf.Al_Ain_International_Airport`
-- * `AIRBASE.PersianGulf.Lavan_Island_Airport`
-- * `AIRBASE.PersianGulf.Jiroft_Airport`
-- * `AIRBASE.PersianGulf.Sas_Al_Nakheel`
-- * `AIRBASE.PersianGulf.Bandar_e_Jask`
-- * `AIRBASE.PersianGulf.Abu_Dhabi_Intl`
-- * `AIRBASE.PersianGulf.Al_Bateen`
-- * `AIRBASE.PersianGulf.Kish_Intl`
-- * `AIRBASE.PersianGulf.Al_Ain_Intl`
-- * `AIRBASE.PersianGulf.Lavan_Island`
-- * `AIRBASE.PersianGulf.Jiroft`
--
-- # Installation
--
@@ -1391,8 +1405,8 @@ end
-- AirbasePoliceCaucasus = ATC_GROUND_PERSIANGULF:New()
--
-- ATC_Ground = ATC_GROUND_PERSIANGULF:New(
-- { AIRBASE.PersianGulf.Kerman_Airport,
-- AIRBASE.PersianGulf.Al_Minhad_AB
-- { AIRBASE.PersianGulf.Kerman,
-- AIRBASE.PersianGulf.Al_Minhad_AFB
-- }
-- )
--
@@ -1441,11 +1455,10 @@ function ATC_GROUND_PERSIANGULF:Start( RepeatScanSeconds )
self.AirbaseMonitor = SCHEDULER:New( self, self._AirbaseMonitor, { self }, 0, RepeatScanSeconds )
end
-- @type ATC_GROUND_MARIANAISLANDS
---
-- @type ATC_GROUND_MARIANAISLANDS
-- @extends #ATC_GROUND
--- # ATC\_GROUND\_MARIANA, extends @{#ATC_GROUND}
--

View File

@@ -619,63 +619,148 @@ ARTY.WeaponType={
}
--- Database of common artillery unit properties.
-- @type ARTY.dbitem
-- @field #string displayname Name displayed in ME.
-- @field #number minrange Minimum firing range in meters.
-- @field #number maxrange Maximum firing range in meters.
-- @field #number reloadtime Reload time in seconds.
--- Database of common artillery unit properties.
-- Table key is the "type name" and table value is and `ARTY.dbitem`.
-- @type ARTY.db
ARTY.db={
["2B11 mortar"] = { -- type "2B11 mortar"
minrange = 500, -- correct?
maxrange = 7000, -- 7 km
reloadtime = 30, -- 30 sec
["LeFH_18-40-105"] = {
displayname = "FH LeFH-18 105mm", -- name displayed in the ME
minrange = 500, -- min range (green circle) in meters
maxrange = 10500, -- max range (red circle) in meters
reloadtime = nil, -- reload time in seconds
},
["SPH 2S1 Gvozdika"] = { -- type "SAU Gvozdika"
minrange = 300, -- correct?
maxrange = 15000, -- 15 km
reloadtime = nil, -- unknown
["M2A1-105"] = {
displayname = "FH M2A1 105mm",
minrange = 500,
maxrange = 11500,
reloadtime = nil,
},
["SPH 2S19 Msta"] = { --type "SAU Msta", alias "2S19 Msta"
minrange = 300, -- correct?
maxrange = 23500, -- 23.5 km
reloadtime = nil, -- unknown
["Pak40"] = {
displayname = "FH Pak 40 75mm",
minrange = 500,
maxrange = 3000,
reloadtime = nil,
},
["L118_Unit"] = {
displayname = "L118 Light Artillery Gun",
minrange = 500,
maxrange = 17500,
reloadtime = nil,
},
["SPH 2S3 Akatsia"] = { -- type "SAU Akatsia", alias "2S3 Akatsia"
minrange = 300, -- correct?
maxrange = 17000, -- 17 km
reloadtime = nil, -- unknown
["Smerch"] = {
displayname = "MLRS 9A52 Smerch CM 300mm",
minrange = 20000,
maxrange = 70000,
reloadtime = 2160,
},
["SPH 2S9 Nona"] = { --type "SAU 2-C9"
minrange = 500, -- correct?
maxrange = 7000, -- 7 km
reloadtime = nil, -- unknown
["Smerch_HE"] = {
displayname = "MLRS 9A52 Smerch HE 300mm",
minrange = 20000,
maxrange = 70000,
reloadtime = 2160,
},
["SPH M109 Paladin"] = { -- type "M-109", alias "M109"
minrange = 300, -- correct?
maxrange = 22000, -- 22 km
reloadtime = nil, -- unknown
["Uragan_BM-27"] = {
displayname = "MLRS 9K57 Uragan BM-27 220mm",
minrange = 11500,
maxrange = 35800,
reloadtime = 840,
},
["SpGH Dana"] = { -- type "SpGH_Dana"
minrange = 300, -- correct?
maxrange = 18700, -- 18.7 km
reloadtime = nil, -- unknown
["Grad-URAL"] = {
displayname = "MLRS BM-21 Grad 122mm",
minrange = 5000,
maxrange = 19000,
reloadtime = 420,
},
["MLRS BM-21 Grad"] = { --type "Grad-URAL", alias "MLRS BM-21 Grad"
minrange = 5000, -- 5 km
maxrange = 19000, -- 19 km
reloadtime = 420, -- 7 min
["HL_B8M1"] = {
displayname = "MLRS HL with B8M1 80mm",
minrange = 500,
maxrange = 5000,
reloadtime = nil,
},
["MLRS 9K57 Uragan BM-27"] = { -- type "Uragan_BM-27"
minrange = 11500, -- 11.5 km
maxrange = 35800, -- 35.8 km
reloadtime = 840, -- 14 min
["tt_B8M1"] = {
displayname = "MLRS LC with B8M1 80mm",
minrange = 500,
maxrange = 5000,
reloadtime = nil,
},
["MLRS 9A52 Smerch"] = { -- type "Smerch"
minrange = 20000, -- 20 km
maxrange = 70000, -- 70 km
reloadtime = 2160, -- 36 min
["MLRS"] = {
displayname = "MLRS M270 227mm",
minrange = 10000,
maxrange = 32000,
reloadtime = 540,
},
["MLRS M270"] = { --type "MRLS", alias "M270 MRLS"
minrange = 10000, -- 10 km
maxrange = 32000, -- 32 km
reloadtime = 540, -- 9 min
["2B11 mortar"] = {
displayname = "Mortar 2B11 120mm",
minrange = 500,
maxrange = 7000,
reloadtime = 30,
},
["PLZ05"] = {
displayname = "PLZ-05",
minrange = 500,
maxrange = 23500,
reloadtime = nil,
},
["SAU Gvozdika"] = {
displayname = "SPH 2S1 Gvozdika 122mm",
minrange = 300,
maxrange = 15000,
reloadtime = nil,
},
["SAU Msta"] = {
displayname = "SPH 2S19 Msta 152mm",
minrange = 300,
maxrange = 23500,
reloadtime = nil,
},
["SAU Akatsia"] = {
displayname = "SPH 2S3 Akatsia 152mm",
minrange = 300,
maxrange = 17000,
reloadtime = nil,
},
["SpGH_Dana"] = {
displayname = "SPH Dana vz77 152mm",
minrange = 300,
maxrange = 18700,
reloadtime = nil,
},
["M-109"] = {
displayname = "SPH M109 Paladin 155mm",
minrange = 300,
maxrange = 22000,
reloadtime = nil,
},
["M12_GMC"] = {
displayname = "SPH M12 GMC 155mm",
minrange = 300,
maxrange = 18200,
reloadtime = nil,
},
["Wespe124"] = {
displayname = "SPH Sd.Kfz.124 Wespe 105mm",
minrange = 300,
maxrange = 7000,
reloadtime = nil,
},
["T155_Firtina"] = {
displayname = "SPH T155 Firtina 155mm",
minrange = 300,
maxrange = 41000,
reloadtime = nil,
},
["SAU 2-C9"] = {
displayname = "SPM 2S9 Nona 120mm M",
minrange = 500,
maxrange = 7000,
reloadtime = nil,
},
}
--- Target.
@@ -695,7 +780,7 @@ ARTY.db={
--- Arty script version.
-- @field #string version
ARTY.version="1.3.1"
ARTY.version="1.3.3"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -797,8 +882,8 @@ function ARTY:New(group, alias)
-- Maximum speed in km/h.
self.SpeedMax=group:GetSpeedMax()
-- Group is mobile or not (e.g. mortars).
if self.SpeedMax>1 then
-- Group is mobile or not (e.g. mortars). Some immobile units have a speed of 1 m/s = 3.6 km/h. So we check this number.
if self.SpeedMax>3.6 then
self.ismobile=true
else
self.ismobile=false
@@ -1923,7 +2008,7 @@ function ARTY:onafterStart(Controllable, From, Event, To)
end
-- Check if we have and arty type that is in the DB.
local _dbproperties=self:_CheckDB(self.DisplayName)
local _dbproperties=self:_CheckDB(self.Type)
self:T({dbproperties=_dbproperties})
if _dbproperties~=nil then
for property,value in pairs(_dbproperties) do
@@ -1969,8 +2054,8 @@ function ARTY:onafterStart(Controllable, From, Event, To)
text=text..string.format("Type = %s\n", self.Type)
text=text..string.format("Display Name = %s\n", self.DisplayName)
text=text..string.format("Number of units = %d\n", self.IniGroupStrength)
text=text..string.format("Speed max = %d km/h\n", self.SpeedMax)
text=text..string.format("Speed default = %d km/h\n", self.Speed)
text=text..string.format("Speed max = %.1f km/h\n", self.SpeedMax)
text=text..string.format("Speed default = %.1f km/h\n", self.Speed)
text=text..string.format("Is mobile = %s\n", tostring(self.ismobile))
text=text..string.format("Is cargo = %s\n", tostring(self.iscargo))
text=text..string.format("Min range = %.1f km\n", self.minrange/1000)
@@ -3049,7 +3134,7 @@ function ARTY:onafterOpenFire(Controllable, From, Event, To, target)
local nfire=Narty
local _type="shots"
if target.weapontype==ARTY.WeaponType.Auto then
nfire=Narty
nfire=Nammo -- We take everything that is available
_type="shots"
elseif target.weapontype==ARTY.WeaponType.Cannon then
nfire=Narty
@@ -3070,6 +3155,8 @@ function ARTY:onafterOpenFire(Controllable, From, Event, To, target)
nfire=Nmissiles
_type="cruise missiles"
end
--env.info(string.format("FF type=%s, Nrockets=%d, Nfire=%d target.nshells=%d", _type, Nrockets, nfire, target.nshells))
-- Adjust if less than requested ammo is left.
target.nshells=math.min(target.nshells, nfire)

View File

@@ -74,7 +74,7 @@
-- @image Designation.JPG
--
-- Date: 24 Oct 2021
-- Last Update: May 2024
-- Last Update: Mar 2025
--
--- Class AUTOLASE
-- @type AUTOLASE
@@ -89,6 +89,10 @@
-- @field #table playermenus
-- @field #boolean smokemenu
-- @field #boolean threatmenu
-- @field #number RoundingPrecision
-- @field #table smokeoffset
-- @field #boolean increasegroundawareness
-- @field #number MonitorFrequency
-- @extends Ops.Intel#INTEL
---
@@ -100,6 +104,9 @@ AUTOLASE = {
alias = "",
debug = false,
smokemenu = true,
RoundingPrecision = 0,
increasegroundawareness = true,
MonitorFrequency = 30,
}
--- Laser spot info
@@ -118,7 +125,7 @@ AUTOLASE = {
--- AUTOLASE class version.
-- @field #string version
AUTOLASE.version = "0.1.25"
AUTOLASE.version = "0.1.31"
-------------------------------------------------------------------
-- Begin Functional.Autolase.lua
@@ -191,6 +198,7 @@ function AUTOLASE:New(RecceSet, Coalition, Alias, PilotSet)
self.reporttimelong = 30
self.smoketargets = false
self.smokecolor = SMOKECOLOR.Red
self.smokeoffset = nil
self.notifypilots = true
self.targetsperrecce = {}
self.RecceUnits = {}
@@ -207,6 +215,11 @@ function AUTOLASE:New(RecceSet, Coalition, Alias, PilotSet)
self.playermenus = {}
self.smokemenu = true
self.threatmenu = true
self.RoundingPrecision = 0
self.increasegroundawareness = true
self.MonitorFrequency = 30
self:EnableSmokeMenu({Angle=math.random(0,359),Distance=math.random(10,20)})
-- Set some string id for output to DCS.log file.
self.lid=string.format("AUTOLASE %s (%s) | ", self.alias, self.coalition and UTILS.GetCoalitionName(self.coalition) or "unknown")
@@ -309,16 +322,41 @@ end
-- Helper Functions
-------------------------------------------------------------------
--- [User] When using Monitor, set the frequency here in which the report will appear
-- @param #AUTOLASE self
-- @param #number Seconds Run the report loop every number of seconds defined here.
-- @return #AUTOLASE self
function AUTOLASE:SetMonitorFrequency(Seconds)
self.MonitorFrequency = Seconds or 30
return self
end
--- [User] Set a table of possible laser codes.
-- Each new RECCE can select a code from this table, default is { 1688, 1130, 4785, 6547, 1465, 4578 } .
-- Each new RECCE can select a code from this table, default is { 1688, 1130, 4785, 6547, 1465, 4578 }.
-- @param #AUTOLASE self
-- @param #list<#number> LaserCodes
-- @return #AUTOLASE
-- @return #AUTOLASE self
function AUTOLASE:SetLaserCodes( LaserCodes )
self.LaserCodes = ( type( LaserCodes ) == "table" ) and LaserCodes or { LaserCodes }
return self
end
--- [User] Improve ground unit detection by using a zone scan and LOS check.
-- @param #AUTOLASE self
-- @return #AUTOLASE self
function AUTOLASE:EnableImproveGroundUnitsDetection()
self.increasegroundawareness = true
return self
end
--- [User] Do not improve ground unit detection by using a zone scan and LOS check.
-- @param #AUTOLASE self
-- @return #AUTOLASE self
function AUTOLASE:DisableImproveGroundUnitsDetection()
self.increasegroundawareness = false
return self
end
--- (Internal) Function to set pilot menu.
-- @param #AUTOLASE self
-- @return #AUTOLASE self
@@ -600,11 +638,26 @@ function AUTOLASE:SetSmokeTargets(OnOff,Color)
return self
end
--- (User) Function to set rounding precision for BR distance output.
-- @param #AUTOLASE self
-- @param #number IDP Rounding precision before/after the decimal sign. Defaults to zero. Positive values round right of the decimal sign, negative ones left of the decimal sign.
-- @return #AUTOLASE self
function AUTOLASE:SetRoundingPrecsion(IDP)
self.RoundingPrecision = IDP or 0
return self
end
--- (User) Show the "Switch smoke target..." menu entry for pilots. On by default.
-- @param #AUTOLASE self
-- @param #table Offset (Optional) Define an offset for the smoke, i.e. not directly on the unit itself, angle is degrees and distance is meters. E.g. `autolase:EnableSmokeMenu({Angle=30,Distance=20})`
-- @return #AUTOLASE self
function AUTOLASE:EnableSmokeMenu()
function AUTOLASE:EnableSmokeMenu(Offset)
self.smokemenu = true
if Offset then
self.smokeoffset = {}
self.smokeoffset.Distance = Offset.Distance or math.random(10,20)
self.smokeoffset.Angle = Offset.Angle or math.random(0,359)
end
return self
end
@@ -613,6 +666,7 @@ end
-- @return #AUTOLASE self
function AUTOLASE:DisableSmokeMenu()
self.smokemenu = false
self.smokeoffset = nil
return self
end
@@ -671,7 +725,8 @@ function AUTOLASE:CleanCurrentLasing()
local unit = recce:GetUnit(1)
local name = unit:GetName()
if not self.RecceUnits[name] then
self.RecceUnits[name] = { name=name, unit=unit, cooldown = false, timestamp = timer.getAbsTime() }
local isground = (unit and unit.IsGround) and unit:IsGround() or false
self.RecceUnits[name] = { name=name, unit=unit, cooldown = false, timestamp = timer.getAbsTime(), isground=isground }
end
end
end
@@ -757,9 +812,11 @@ function AUTOLASE:ShowStatus(Group,Unit)
end
local code = self:GetLaserCode(unit:GetName())
report:Add(string.format("Recce %s has code %d",name,code))
report:Add("---------------")
end
end
report:Add(string.format("Lasing min threat level %d",self.minthreatlevel))
report:Add("---------------")
local lines = 0
for _ind,_entry in pairs(self.CurrentLasing) do
local entry = _entry -- #AUTOLASE.LaserSpot
@@ -779,22 +836,28 @@ function AUTOLASE:ShowStatus(Group,Unit)
if playername then
local settings = _DATABASE:GetPlayerSettings(playername)
if settings then
self:I("Get Settings ok!")
self:T("Get Settings ok!")
if settings:IsA2G_MGRS() then
locationstring = entry.coordinate:ToStringMGRS(settings)
elseif settings:IsA2G_LL_DMS() then
locationstring = entry.coordinate:ToStringLLDMS(settings)
elseif settings:IsA2G_LL_DDM() then
locationstring = entry.coordinate:ToStringLLDDM(settings)
elseif settings:IsA2G_BR() then
locationstring = entry.coordinate:ToStringBR(Group:GetCoordinate() or Unit:GetCoordinate(),settings)
-- attention this is the distance from the ASKING unit to target, not from RECCE to target!
local startcoordinate = Unit:GetCoordinate() or Group:GetCoordinate()
locationstring = entry.coordinate:ToStringBR(startcoordinate,settings,false,self.RoundingPrecision)
end
end
end
local text = string.format("%s lasing %s code %d\nat %s",reccename,typename,code,locationstring)
local text = string.format("+ %s lasing %s code %d\nat %s",reccename,typename,code,locationstring)
report:Add(text)
report:Add("---------------")
lines = lines + 1
end
if lines == 0 then
report:Add("No targets!")
report:Add("---------------")
end
local reporttime = self.reporttimelong
if lines == 0 then reporttime = self.reporttimeshort end
@@ -913,6 +976,65 @@ function AUTOLASE:CanLase(Recce,Unit)
return canlase
end
--- (Internal) Function to do a zone check per ground Recce and make found units and statics "known".
-- @param #AUTOLASE self
-- @return #AUTOLASE self
function AUTOLASE:_Prescient()
-- self.RecceUnits[name] = { name=name, unit=unit, cooldown = false, timestamp = timer.getAbsTime(), isground=isground }
for _,_data in pairs(self.RecceUnits) do
-- ground units only
if _data.isground and _data.unit and _data.unit:IsAlive() then
local unit = _data.unit -- Wrapper.Unit#UNIT
local position = unit:GetCoordinate() -- Core.Point#COORDINATE
local needsinit = false
if position then
local lastposition = unit:GetProperty("lastposition")
-- property initiated?
if not lastposition then
unit:SetProperty("lastposition",position)
lastposition = position
needsinit = true
end
-- has moved?
local dist = position:Get2DDistance(lastposition)
-- refresh?
local TNow = timer.getAbsTime()
-- check
if dist > 10 or needsinit==true or TNow - _data.timestamp > 29 then
-- init scan objects
local hasunits,hasstatics,_,Units,Statics = position:ScanObjects(self.LaseDistance,true,true,false)
-- loop found units
if hasunits then
self:T(self.lid.."Checking possibly visible UNITs for Recce "..unit:GetName())
for _,_target in pairs(Units) do -- Wrapper.Unit#UNIT object here
local target = _target -- Wrapper.Unit#UNIT
if target and target:GetCoalition() ~= self.coalition then
if unit:IsLOS(target) and (not target:IsUnitDetected(unit))then
unit:KnowUnit(target,true,true)
end
end
end
end
-- loop found statics
if hasstatics then
self:T(self.lid.."Checking possibly visible STATICs for Recce "..unit:GetName())
for _,_static in pairs(Statics) do -- DCS static object here
local static = STATIC:Find(_static)
if static and static:GetCoalition() ~= self.coalition then
local IsLOS = position:IsLOS(static:GetCoordinate())
if IsLOS then
unit:KnowUnit(static,true,true)
end
end
end
end
end
end
end
end
return self
end
-------------------------------------------------------------------
-- FSM Functions
-------------------------------------------------------------------
@@ -925,6 +1047,9 @@ end
-- @return #AUTOLASE self
function AUTOLASE:onbeforeMonitor(From, Event, To)
self:T({From, Event, To})
if self.increasegroundawareness then
self:_Prescient()
end
-- Check if group has detected any units.
self:UpdateIntel()
return self
@@ -953,7 +1078,7 @@ function AUTOLASE:onafterMonitor(From, Event, To)
local grp = contact.group
local coord = contact.position
local reccename = contact.recce or "none"
local threat = contact.threatlevel or 0
local threat = contact.threatlevel or 0
local reccegrp = UNIT:FindByName(reccename)
if reccegrp then
local reccecoord = reccegrp:GetCoordinate()
@@ -1077,6 +1202,9 @@ function AUTOLASE:onafterMonitor(From, Event, To)
}
if self.smoketargets then
local coord = unit:GetCoordinate()
if self.smokeoffset then
coord:Translate(self.smokeoffset.Distance,self.smokeoffset.Angle,true,true)
end
local color = self:GetSmokeColor(reccename)
coord:Smoke(color)
end
@@ -1087,7 +1215,8 @@ function AUTOLASE:onafterMonitor(From, Event, To)
end
end
self:__Monitor(-30)
local nextloop = -self.MonitorFrequency or -30
self:__Monitor(nextloop)
return self
end

View File

@@ -357,7 +357,7 @@ function CLEANUP_AIRBASE.__:EventAddForCleanUp( Event )
self:F({Event})
if Event.IniDCSUnit and Event.IniCategory == Object.Category.UNIT then
if Event.IniDCSUnit and Event.IniUnit and Event.IniCategory == Object.Category.UNIT then
if self.CleanUpList[Event.IniDCSUnitName] == nil then
if self:IsInAirbase( Event.IniUnit:GetVec2() ) then
self:AddForCleanUp( Event.IniUnit, Event.IniDCSUnitName )
@@ -365,7 +365,7 @@ function CLEANUP_AIRBASE.__:EventAddForCleanUp( Event )
end
end
if Event.TgtDCSUnit and Event.TgtCategory == Object.Category.UNIT then
if Event.TgtDCSUnit and Event.TgtUnit and Event.TgtCategory == Object.Category.UNIT then
if self.CleanUpList[Event.TgtDCSUnitName] == nil then
if self:IsInAirbase( Event.TgtUnit:GetVec2() ) then
self:AddForCleanUp( Event.TgtUnit, Event.TgtDCSUnitName )
@@ -387,7 +387,7 @@ function CLEANUP_AIRBASE.__:CleanUpSchedule()
local CleanUpUnit = CleanUpListData.CleanUpUnit -- Wrapper.Unit#UNIT
local CleanUpGroupName = CleanUpListData.CleanUpGroupName
if CleanUpUnit:IsAlive() ~= nil then
if CleanUpUnit and CleanUpUnit:IsAlive() ~= nil then
if self:IsInAirbase( CleanUpUnit:GetVec2() ) then
@@ -414,7 +414,7 @@ function CLEANUP_AIRBASE.__:CleanUpSchedule()
end
end
-- Clean Units which are waiting for a very long time in the CleanUpZone.
if CleanUpUnit and not CleanUpUnit:GetPlayerName() then
if CleanUpUnit and (CleanUpUnit.GetPlayerName == nil or not CleanUpUnit:GetPlayerName()) then
local CleanUpUnitVelocity = CleanUpUnit:GetVelocityKMH()
if CleanUpUnitVelocity < 1 then
if CleanUpListData.CleanUpMoved then

View File

@@ -652,7 +652,7 @@ do -- DETECTION_BASE
if DetectedObject:isExist() then
local TargetIsDetected, TargetIsVisible, TargetLastTime, TargetKnowType, TargetKnowDistance, TargetLastPos, TargetLastVelocity = DetectionUnit:IsTargetDetected(
local TargetIsDetected, TargetIsVisible, TargetKnowType, TargetKnowDistance, TargetLastTime, TargetLastPos, TargetLastVelocity = DetectionUnit:IsTargetDetected(
DetectedObject,
self.DetectVisual,
self.DetectOptical,

View File

@@ -1154,8 +1154,6 @@ function ESCORT:_ReportTargetsScheduler()
if self.EscortGroup:IsAlive() and self.EscortClient:IsAlive() then
if true then
local EscortGroupName = self.EscortGroup:GetName()
self.EscortMenuAttackNearbyTargets:RemoveSubMenus()
@@ -1226,177 +1224,6 @@ function ESCORT:_ReportTargetsScheduler()
end
return true
else
-- local EscortGroupName = self.EscortGroup:GetName()
-- local EscortTargets = self.EscortGroup:GetDetectedTargets()
--
-- local ClientEscortTargets = self.EscortClient._EscortGroups[EscortGroupName].Targets
--
-- local EscortTargetMessages = ""
-- for EscortTargetID, EscortTarget in pairs( EscortTargets ) do
-- local EscortObject = EscortTarget.object
-- self:T( EscortObject )
-- if EscortObject and EscortObject:isExist() and EscortObject.id_ < 50000000 then
--
-- local EscortTargetUnit = UNIT:Find( EscortObject )
-- local EscortTargetUnitName = EscortTargetUnit:GetName()
--
--
--
-- -- local EscortTargetIsDetected,
-- -- EscortTargetIsVisible,
-- -- EscortTargetLastTime,
-- -- EscortTargetKnowType,
-- -- EscortTargetKnowDistance,
-- -- EscortTargetLastPos,
-- -- EscortTargetLastVelocity
-- -- = self.EscortGroup:IsTargetDetected( EscortObject )
-- --
-- -- self:T( { EscortTargetIsDetected,
-- -- EscortTargetIsVisible,
-- -- EscortTargetLastTime,
-- -- EscortTargetKnowType,
-- -- EscortTargetKnowDistance,
-- -- EscortTargetLastPos,
-- -- EscortTargetLastVelocity } )
--
--
-- local EscortTargetUnitVec3 = EscortTargetUnit:GetVec3()
-- local EscortVec3 = self.EscortGroup:GetVec3()
-- local Distance = ( ( EscortTargetUnitVec3.x - EscortVec3.x )^2 +
-- ( EscortTargetUnitVec3.y - EscortVec3.y )^2 +
-- ( EscortTargetUnitVec3.z - EscortVec3.z )^2
-- ) ^ 0.5 / 1000
--
-- self:T( { self.EscortGroup:GetName(), EscortTargetUnit:GetName(), Distance, EscortTarget } )
--
-- if Distance <= 15 then
--
-- if not ClientEscortTargets[EscortTargetUnitName] then
-- ClientEscortTargets[EscortTargetUnitName] = {}
-- end
-- ClientEscortTargets[EscortTargetUnitName].AttackUnit = EscortTargetUnit
-- ClientEscortTargets[EscortTargetUnitName].visible = EscortTarget.visible
-- ClientEscortTargets[EscortTargetUnitName].type = EscortTarget.type
-- ClientEscortTargets[EscortTargetUnitName].distance = EscortTarget.distance
-- else
-- if ClientEscortTargets[EscortTargetUnitName] then
-- ClientEscortTargets[EscortTargetUnitName] = nil
-- end
-- end
-- end
-- end
--
-- self:T( { "Sorting Targets Table:", ClientEscortTargets } )
-- table.sort( ClientEscortTargets, function( a, b ) return a.Distance < b.Distance end )
-- self:T( { "Sorted Targets Table:", ClientEscortTargets } )
--
-- -- Remove the sub menus of the Attack menu of the Escort for the EscortGroup.
-- self.EscortMenuAttackNearbyTargets:RemoveSubMenus()
--
-- if self.EscortMenuTargetAssistance then
-- self.EscortMenuTargetAssistance:RemoveSubMenus()
-- end
--
-- --for MenuIndex = 1, #self.EscortMenuAttackTargets do
-- -- self:T( { "Remove Menu:", self.EscortMenuAttackTargets[MenuIndex] } )
-- -- self.EscortMenuAttackTargets[MenuIndex] = self.EscortMenuAttackTargets[MenuIndex]:Remove()
-- --end
--
--
-- if ClientEscortTargets then
-- for ClientEscortTargetUnitName, ClientEscortTargetData in pairs( ClientEscortTargets ) do
--
-- for ClientEscortGroupName, EscortGroupData in pairs( self.EscortClient._EscortGroups ) do
--
-- if ClientEscortTargetData and ClientEscortTargetData.AttackUnit:IsAlive() then
--
-- local EscortTargetMessage = ""
-- local EscortTargetCategoryName = ClientEscortTargetData.AttackUnit:GetCategoryName()
-- local EscortTargetCategoryType = ClientEscortTargetData.AttackUnit:GetTypeName()
-- if ClientEscortTargetData.type then
-- EscortTargetMessage = EscortTargetMessage .. EscortTargetCategoryName .. " (" .. EscortTargetCategoryType .. ") at "
-- else
-- EscortTargetMessage = EscortTargetMessage .. "Unknown target at "
-- end
--
-- local EscortTargetUnitVec3 = ClientEscortTargetData.AttackUnit:GetVec3()
-- local EscortVec3 = self.EscortGroup:GetVec3()
-- local Distance = ( ( EscortTargetUnitVec3.x - EscortVec3.x )^2 +
-- ( EscortTargetUnitVec3.y - EscortVec3.y )^2 +
-- ( EscortTargetUnitVec3.z - EscortVec3.z )^2
-- ) ^ 0.5 / 1000
--
-- self:T( { self.EscortGroup:GetName(), ClientEscortTargetData.AttackUnit:GetName(), Distance, ClientEscortTargetData.AttackUnit } )
-- if ClientEscortTargetData.visible == false then
-- EscortTargetMessage = EscortTargetMessage .. string.format( "%.2f", Distance ) .. " estimated km"
-- else
-- EscortTargetMessage = EscortTargetMessage .. string.format( "%.2f", Distance ) .. " km"
-- end
--
-- if ClientEscortTargetData.visible then
-- EscortTargetMessage = EscortTargetMessage .. ", visual"
-- end
--
-- if ClientEscortGroupName == EscortGroupName then
--
-- MENU_GROUP_COMMAND:New( self.EscortClient,
-- EscortTargetMessage,
-- self.EscortMenuAttackNearbyTargets,
-- ESCORT._AttackTarget,
-- { ParamSelf = self,
-- ParamUnit = ClientEscortTargetData.AttackUnit
-- }
-- )
-- EscortTargetMessages = EscortTargetMessages .. "\n - " .. EscortTargetMessage
-- else
-- if self.EscortMenuTargetAssistance then
-- local MenuTargetAssistance = MENU_GROUP:New( self.EscortClient, EscortGroupData.EscortName, self.EscortMenuTargetAssistance )
-- MENU_GROUP_COMMAND:New( self.EscortClient,
-- EscortTargetMessage,
-- MenuTargetAssistance,
-- ESCORT._AssistTarget,
-- self,
-- EscortGroupData.EscortGroup,
-- ClientEscortTargetData.AttackUnit
-- )
-- end
-- end
-- else
-- ClientEscortTargetData = nil
-- end
-- end
-- end
--
-- if EscortTargetMessages ~= "" and self.ReportTargets == true then
-- self.EscortGroup:MessageToClient( "Detected targets within 15 km range:" .. EscortTargetMessages:gsub("\n$",""), 20, self.EscortClient )
-- else
-- self.EscortGroup:MessageToClient( "No targets detected!", 20, self.EscortClient )
-- end
-- end
--
-- if self.EscortMenuResumeMission then
-- self.EscortMenuResumeMission:RemoveSubMenus()
--
-- -- if self.EscortMenuResumeWayPoints then
-- -- for MenuIndex = 1, #self.EscortMenuResumeWayPoints do
-- -- self:T( { "Remove Menu:", self.EscortMenuResumeWayPoints[MenuIndex] } )
-- -- self.EscortMenuResumeWayPoints[MenuIndex] = self.EscortMenuResumeWayPoints[MenuIndex]:Remove()
-- -- end
-- -- end
--
-- local TaskPoints = self:RegisterRoute()
-- for WayPointID, WayPoint in pairs( TaskPoints ) do
-- local EscortVec3 = self.EscortGroup:GetVec3()
-- local Distance = ( ( WayPoint.x - EscortVec3.x )^2 +
-- ( WayPoint.y - EscortVec3.z )^2
-- ) ^ 0.5 / 1000
-- MENU_GROUP_COMMAND:New( self.EscortClient, "Waypoint " .. WayPointID .. " at " .. string.format( "%.2f", Distance ).. "km", self.EscortMenuResumeMission, ESCORT._ResumeMission, { ParamSelf = self, ParamWayPoint = WayPointID } )
-- end
-- end
--
-- return true
end
end
return false

View File

@@ -1060,7 +1060,7 @@ function FOX:onafterMissileLaunch(From, Event, To, missile)
-- Tracking info and init of last bomb position.
local text=string.format("FOX: Tracking missile %s(%s) - target %s - shooter %s", missile.missileType, missile.missileName, tostring(missile.targetName), missile.shooterName)
self:I(FOX.lid..text)
self:T(FOX.lid..text)
MESSAGE:New(text, 10):ToAllIf(self.Debug)
-- Loop over players.

View File

@@ -22,7 +22,7 @@
-- @module Functional.Mantis
-- @image Functional.Mantis.jpg
--
-- Last Update: July 2024
-- Last Update: May 2025
-------------------------------------------------------------------------
--- **MANTIS** class, extends Core.Base#BASE
@@ -60,6 +60,11 @@
-- @field #number ShoradActDistance Distance of an attacker in meters from a Mantis SAM site, on which Shorad will be switched on. Useful to not give away Shorad sites too early. Default 15km. Should be smaller than checkradius.
-- @field #boolean checkforfriendlies If true, do not activate a SAM installation if a friendly aircraft is in firing range.
-- @field #table FilterZones Table of Core.Zone#ZONE Zones Consider SAM groups in this zone(s) only for this MANTIS instance, must be handed as #table of Zone objects.
-- @field #boolean SmokeDecoy If true, smoke short range SAM units as decoy if a plane is in firing range.
-- @field #number SmokeDecoyColor Color to use, defaults to SMOKECOLOR.White
-- @field #number checkcounter Counter for SAM Table refreshes.
-- @field #number DLinkCacheTime Seconds after which cached contacts in DLink will decay.
-- @field #boolean logsamstatus Log SAM status in dcs.log every cycle if true
-- @extends Core.Base#BASE
@@ -71,10 +76,9 @@
--
-- * Moose derived Modular, Automatic and Network capable Targeting and Interception System.
-- * Controls a network of SAM sites. Uses detection to switch on the SAM site closest to the enemy.
-- * **Automatic mode** (default since 0.8) can set-up your SAM site network automatically for you
-- * **Classic mode** behaves like before
-- * Leverage evasiveness from SEAD, leverage attack range setting
-- * Automatic setup of SHORAD based on groups of the class "short-range"
-- * **Automatic mode** (default) will set-up your SAM site network automatically for you.
-- * Leverage evasiveness from SEAD, leverage attack range setting.
-- * Automatic setup of SHORAD based on groups of the class "short-range".
--
-- # 0. Base considerations and naming conventions
--
@@ -86,6 +90,7 @@
-- * SAM sites, e.g. each **group name** begins with "Red SAM"
-- * EWR network and AWACS, e.g. each **group name** begins with "Red EWR" and *not* e.g. "Red SAM EWR" (overlap with "Red SAM"), "Red EWR Awacs" will be found by "Red EWR"
-- * SHORAD, e.g. each **group name** begins with "Red SHORAD" and *not" e.g. just "SHORAD" because you might also have "Blue SHORAD"
-- * Point Defense, e.g. each **group name** begins with "Red AAA" and *not" e.g. just "AAA" because you might also have "Blue AAA"
--
-- It's important to get this right because of the nature of the filter-system in @{Core.Set#SET_GROUP}. Filters are "greedy", that is they
-- will match *any* string that contains the search string - hence we need to avoid that SAMs, EWR and SHORAD step on each other\'s toes.
@@ -129,10 +134,10 @@
--
-- # 0.1 Set-up in the mission editor
--
-- Set up your SAM sites in the mission editor. Name the groups using a systematic approach like above.
-- Set up your EWR system in the mission editor. Name the groups using a systematic approach like above. Can be e.g. AWACS or a combination of AWACS and Search Radars like e.g. EWR 1L13 etc.
-- Set up your SAM sites in the mission editor. Name the groups using a systematic approach like above.Can be e.g. AWACS or a combination of AWACS and Search Radars like e.g. EWR 1L13 etc.
-- Search Radars usually have "SR" or "STR" in their names. Use the encyclopedia in the mission editor to inform yourself.
-- Set up your SHORAD systems. They need to be **close** to (i.e. around) the SAM sites to be effective. Use **one** group per SAM location. SA-15 TOR systems offer a good missile defense.
-- Set up your SHORAD systems. They need to be **close** to (i.e. around) the SAM sites to be effective. Use **one unit ** per group (multiple groups) for the SAM location.
-- Else, evasive manoevers might club up all defenders in one place. Red SA-15 TOR systems offer a good missile defense.
--
-- [optional] Set up your HQ. Can be any group, e.g. a command vehicle.
--
@@ -144,6 +149,7 @@
-- **Location** is of highest importance here. Whilst AWACS in DCS has almost the "all seeing eye", EWR don't have that. Choose your location wisely, against a mountain backdrop or inside a valley even the best EWR system
-- doesn't work well. Prefer higher-up locations with a good view; use F7 in-game to check where you actually placed your EWR and have a look around. Apart from the obvious choice, do also consider other radar units
-- for this role, most have "SR" (search radar) or "STR" (search and track radar) in their names, use the encyclopedia to see what they actually do.
-- **HINT** Set at least one EWR on invisible and immortal so MANTIS doesn't stop working.
--
-- ## 1.2 SAM sites
--
@@ -183,7 +189,7 @@
--
-- ## 2.1 Auto mode features
--
-- ### 2.1.1 You can now add Accept-, Reject- and Conflict-Zones to your setup, e.g. to consider borders or de-militarized zones:
-- ### 2.1.1 You can add Accept-, Reject- and Conflict-Zones to your setup, e.g. to consider borders or de-militarized zones:
--
-- -- Parameters are tables of Core.Zone#ZONE objects!
-- -- This is effectively a 3-stage filter allowing for zone overlap. A coordinate is accepted first when
@@ -192,31 +198,32 @@
-- mybluemantis:AddZones(AcceptZones,RejectZones,ConflictZones)
--
--
-- ### 2.1.2 Change the number of long-, mid- and short-range systems going live on a detected target:
-- ### 2.1.2 Change the number of long-, mid- and short-range, point defense systems going live on a detected target:
--
-- -- parameters are numbers. Defaults are 1,2,2,6 respectively
-- mybluemantis:SetMaxActiveSAMs(Short,Mid,Long,Classic)
-- -- parameters are numbers. Defaults are 1,2,2,6,6 respectively
-- mybluemantis:SetMaxActiveSAMs(Short,Mid,Long,Classic,Point)
--
-- ### 2.1.3 SHORAD will automatically be added from SAM sites of type "short-range"
-- ### 2.1.3 SHORAD/Point defense will automatically be added from SAM sites of type "point" or if the range is less than 5km or if the type is AAA.
--
-- ### 2.1.4 Advanced features
--
-- -- switch off auto mode **before** you start MANTIS.
-- mybluemantis.automode = false
--
-- -- switch off auto shorad **before** you start MANTIS.
-- mybluemantis.autoshorad = false
--
-- -- scale of the activation range, i.e. don't activate at the fringes of max range, defaults below.
-- -- Option to set the scale of the activation range, i.e. don't activate at the fringes of max range, defaults below.
-- -- also see engagerange below.
-- self.radiusscale[MANTIS.SamType.LONG] = 1.1
-- self.radiusscale[MANTIS.SamType.MEDIUM] = 1.2
-- self.radiusscale[MANTIS.SamType.SHORT] = 1.3
-- self.radiusscale[MANTIS.SamType.POINT] = 1.4
--
-- ### 2.1.5 Friendlies check in firing range
--
-- -- For some scenarios, like Cold War, it might be useful not to activate SAMs if friendly aircraft are around to avoid death by friendly fire.
-- mybluemantis.checkforfriendlies = true
--
-- ### 2.1.6 Shoot & Scoot
--
-- -- Option to make the (driveable) SHORAD units drive around and shuffle positions
-- -- We use a SET_ZONE for that, number of zones to consider defaults to three, Random is true for random coordinates and Formation is e.g. "Vee".
-- mybluemantis:AddScootZones(ZoneSet, Number, Random, Formation)
--
-- # 3. Default settings [both modes unless stated otherwise]
--
@@ -239,26 +246,8 @@
-- E.g. mymantis:SetAdvancedMode( true, 90 )
--
-- Use this option if you want to make use of or allow advanced SEAD tactics.
--
-- # 5. Integrate SHORAD [classic mode, not necessary in automode]
--
-- You can also choose to integrate Mantis with @{Functional.Shorad#SHORAD} for protection against HARMs and AGMs. When SHORAD detects a missile fired at one of MANTIS' SAM sites, it will activate SHORAD systems in
-- the given defense checkradius around that SAM site. Create a SHORAD object first, then integrate with MANTIS like so:
--
-- local SamSet = SET_GROUP:New():FilterPrefixes("Blue SAM"):FilterCoalitions("blue"):FilterStart()
-- myshorad = SHORAD:New("BlueShorad", "Blue SHORAD", SamSet, 22000, 600, "blue")
-- -- now set up MANTIS
-- mymantis = MANTIS:New("BlueMantis","Blue SAM","Blue EWR",nil,"blue",false,"Blue Awacs")
-- mymantis:AddShorad(myshorad,720)
-- mymantis:Start()
--
-- If you systematically name your SHORAD groups starting with "Blue SHORAD" you'll need exactly **one** SHORAD instance to manage all SHORAD groups.
--
-- (Optionally) you can remove the link later on with
--
-- mymantis:RemoveShorad()
--
-- # 6. Integrated SEAD
-- # 5. Integrated SEAD
--
-- MANTIS is using @{Functional.Sead#SEAD} internally to both detect and evade HARM attacks. No extra efforts needed to set this up!
-- Once a HARM attack is detected, MANTIS (via SEAD) will shut down the radars of the attacked SAM site and take evasive action by moving the SAM
@@ -296,6 +285,7 @@ MANTIS = {
SAM_Table_Long = {},
SAM_Table_Medium = {},
SAM_Table_Short = {},
SAM_Table_PointDef = {},
lid = "",
Detection = nil,
AWACS_Detection = nil,
@@ -329,6 +319,11 @@ MANTIS = {
autoshorad = true,
ShoradGroupSet = nil,
checkforfriendlies = false,
SmokeDecoy = false,
SmokeDecoyColor = SMOKECOLOR.White,
checkcounter = 1,
DLinkCacheTime = 120,
logsamstatus = false,
}
--- Advanced state enumerator
@@ -345,8 +340,17 @@ MANTIS.SamType = {
SHORT = "Short",
MEDIUM = "Medium",
LONG = "Long",
POINT = "Point",
}
--- SAM Radiusscale
-- @type MANTIS.radiusscale
MANTIS.radiusscale = {}
MANTIS.radiusscale[MANTIS.SamType.LONG] = 1.1
MANTIS.radiusscale[MANTIS.SamType.MEDIUM] = 1.2
MANTIS.radiusscale[MANTIS.SamType.SHORT] = 1.75
MANTIS.radiusscale[MANTIS.SamType.POINT] = 3
--- SAM data
-- @type MANTIS.SamData
-- @field #number Range Max firing range in km
@@ -354,10 +358,11 @@ MANTIS.SamType = {
-- @field #number Height Max firing height in km
-- @field #string Type #MANTIS.SamType of SAM, i.e. SHORT, MEDIUM or LONG (range)
-- @field #string Radar Radar typename on unit level (used as key)
-- @field #string Point Point defense capable
MANTIS.SamData = {
["Hawk"] = { Range=35, Blindspot=0, Height=12, Type="Medium", Radar="Hawk" }, -- measures in km
["NASAMS"] = { Range=14, Blindspot=0, Height=7, Type="Short", Radar="NSAMS" }, -- AIM 120B
["Patriot"] = { Range=99, Blindspot=0, Height=25, Type="Long", Radar="Patriot" },
["Patriot"] = { Range=99, Blindspot=0, Height=25, Type="Long", Radar="Patriot str" },
["Rapier"] = { Range=10, Blindspot=0, Height=3, Type="Short", Radar="rapier" },
["SA-2"] = { Range=40, Blindspot=7, Height=25, Type="Medium", Radar="S_75M_Volhov" },
["SA-3"] = { Range=18, Blindspot=6, Height=18, Type="Short", Radar="5p73 s-125 ln" },
@@ -365,24 +370,25 @@ MANTIS.SamData = {
["SA-6"] = { Range=25, Blindspot=0, Height=8, Type="Medium", Radar="1S91" },
["SA-10"] = { Range=119, Blindspot=0, Height=18, Type="Long" , Radar="S-300PS 4"},
["SA-11"] = { Range=35, Blindspot=0, Height=20, Type="Medium", Radar="SA-11" },
["Roland"] = { Range=5, Blindspot=0, Height=5, Type="Short", Radar="Roland" },
["Roland"] = { Range=6, Blindspot=0, Height=5, Type="Short", Radar="Roland" },
["Gepard"] = { Range=5, Blindspot=0, Height=4, Type="Point", Radar="Gepard" },
["HQ-7"] = { Range=12, Blindspot=0, Height=3, Type="Short", Radar="HQ-7" },
["SA-9"] = { Range=4, Blindspot=0, Height=3, Type="Short", Radar="Strela" },
["SA-9"] = { Range=4, Blindspot=0, Height=3, Type="Point", Radar="Strela", Point="true" },
["SA-8"] = { Range=10, Blindspot=0, Height=5, Type="Short", Radar="Osa 9A33" },
["SA-19"] = { Range=8, Blindspot=0, Height=3, Type="Short", Radar="Tunguska" },
["SA-15"] = { Range=11, Blindspot=0, Height=6, Type="Short", Radar="Tor 9A331" },
["SA-13"] = { Range=5, Blindspot=0, Height=3, Type="Short", Radar="Strela" },
["SA-15"] = { Range=11, Blindspot=0, Height=6, Type="Point", Radar="Tor 9A331", Point="true" },
["SA-13"] = { Range=5, Blindspot=0, Height=3, Type="Point", Radar="Strela", Point="true" },
["Avenger"] = { Range=4, Blindspot=0, Height=3, Type="Short", Radar="Avenger" },
["Chaparral"] = { Range=8, Blindspot=0, Height=3, Type="Short", Radar="Chaparral" },
["Linebacker"] = { Range=4, Blindspot=0, Height=3, Type="Short", Radar="Linebacker" },
["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" },
["HEMTT_C-RAM_Phalanx"] = { Range=2, Blindspot=0, Height=2, Type="Point", Radar="HEMTT_C-RAM_Phalanx", Point="true" },
-- units from HDS Mod, multi launcher options is tricky
["SA-10B"] = { Range=75, Blindspot=0, Height=18, Type="Medium" , Radar="SA-10B"},
["SA-17"] = { Range=50, Blindspot=3, Height=30, Type="Medium", Radar="SA-17" },
["SA-20A"] = { Range=150, Blindspot=5, Height=27, Type="Long" , Radar="S-300PMU1"},
["SA-20B"] = { Range=200, Blindspot=4, Height=27, Type="Long" , Radar="S-300PMU2"},
["HQ-2"] = { Range=50, Blindspot=6, Height=35, Type="Medium", Radar="HQ_2_Guideline_LN" },
["SHORAD"] = { Range=3, Blindspot=0, Height=3, Type="Short", Radar="Igla" },
["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" },
}
@@ -394,6 +400,7 @@ MANTIS.SamData = {
-- @field #number Height Max firing height in km
-- @field #string Type #MANTIS.SamType of SAM, i.e. SHORT, MEDIUM or LONG (range)
-- @field #string Radar Radar typename on unit level (used as key)
-- @field #string Point Point defense capable
MANTIS.SamDataHDS = {
-- units from HDS Mod, multi launcher options is tricky
-- group name MUST contain HDS to ID launcher type correctly!
@@ -415,20 +422,21 @@ MANTIS.SamDataHDS = {
-- @field #number Height Max firing height in km
-- @field #string Type #MANTIS.SamType of SAM, i.e. SHORT, MEDIUM or LONG (range)
-- @field #string Radar Radar typename on unit level (used as key)
-- @field #string Point Point defense capable
MANTIS.SamDataSMA = {
-- units from SMA Mod (Sweedish Military Assets)
-- https://forum.dcs.world/topic/295202-swedish-military-assets-for-dcs-by-currenthill/
-- group name MUST contain SMA to ID launcher type correctly!
["RBS98M SMA"] = { Range=20, Blindspot=0, Height=8, Type="Short", Radar="RBS-98" },
["RBS70 SMA"] = { Range=8, Blindspot=0, Height=5.5, Type="Short", Radar="RBS-70" },
["RBS70M SMA"] = { Range=8, Blindspot=0, Height=5.5, Type="Short", Radar="BV410_RBS70" },
["RBS90 SMA"] = { Range=8, Blindspot=0, Height=5.5, Type="Short", Radar="RBS-90" },
["RBS90M SMA"] = { Range=8, Blindspot=0, Height=5.5, Type="Short", Radar="BV410_RBS90" },
["RBS103A SMA"] = { Range=150, Blindspot=3, Height=24.5, Type="Long", Radar="LvS-103_Lavett103_Rb103A" },
["RBS103B SMA"] = { Range=35, Blindspot=0, Height=36, Type="Medium", Radar="LvS-103_Lavett103_Rb103B" },
["RBS103AM SMA"] = { Range=150, Blindspot=3, Height=24.5, Type="Long", Radar="LvS-103_Lavett103_HX_Rb103A" },
["RBS103BM SMA"] = { Range=35, Blindspot=0, Height=36, Type="Medium", Radar="LvS-103_Lavett103_HX_Rb103B" },
["Lvkv9040M SMA"] = { Range=4, Blindspot=0, Height=2.5, Type="Short", Radar="LvKv9040" },
["RBS98M SMA"] = { Range=20, Blindspot=0.2, Height=8, Type="Short", Radar="RBS-98" },
["RBS70 SMA"] = { Range=8, Blindspot=0.25, Height=6, Type="Short", Radar="RBS-70" },
["RBS70M SMA"] = { Range=8, Blindspot=0.25, Height=6, Type="Short", Radar="BV410_RBS70" },
["RBS90 SMA"] = { Range=8, Blindspot=0.25, Height=6, Type="Short", Radar="RBS-90" },
["RBS90M SMA"] = { Range=8, Blindspot=0.25, Height=6, Type="Short", Radar="BV410_RBS90" },
["RBS103A SMA"] = { Range=160, Blindspot=1, Height=36, Type="Long", Radar="LvS-103_Lavett103_Rb103A" },
["RBS103B SMA"] = { Range=120, Blindspot=3, Height=24.5, Type="Long", Radar="LvS-103_Lavett103_Rb103B" },
["RBS103AM SMA"] = { Range=160, Blindspot=1, Height=36, Type="Long", Radar="LvS-103_Lavett103_HX_Rb103A" },
["RBS103BM SMA"] = { Range=120, Blindspot=3, Height=24.5, Type="Long", Radar="LvS-103_Lavett103_HX_Rb103B" },
["Lvkv9040M SMA"] = { Range=2, Blindspot=0.1, Height=1.2, Type="Point", Radar="LvKv9040",Point="true" },
}
--- SAM data CH
@@ -438,28 +446,54 @@ MANTIS.SamDataSMA = {
-- @field #number Height Max firing height in km
-- @field #string Type #MANTIS.SamType of SAM, i.e. SHORT, MEDIUM or LONG (range)
-- @field #string Radar Radar typename on unit level (used as key)
-- @field #string Point Point defense capable
MANTIS.SamDataCH = {
-- units from CH (Military Assets by Currenthill)
-- https://www.currenthill.com/
-- group name MUST contain CHM to ID launcher type correctly!
["2S38 CH"] = { Range=8, Blindspot=0.5, Height=6, Type="Short", Radar="2S38" },
["PantsirS1 CH"] = { Range=20, Blindspot=1.2, Height=15, Type="Short", Radar="PantsirS1" },
["PantsirS2 CH"] = { Range=30, Blindspot=1.2, Height=18, Type="Medium", Radar="PantsirS2" },
["PGL-625 CH"] = { Range=10, Blindspot=0.5, Height=5, Type="Short", Radar="PGL_625" },
["HQ-17A CH"] = { Range=20, Blindspot=1.5, Height=10, Type="Short", Radar="HQ17A" },
["M903PAC2 CH"] = { Range=160, Blindspot=3, Height=24.5, Type="Long", Radar="MIM104_M903_PAC2" },
["M903PAC3 CH"] = { Range=120, Blindspot=1, Height=40, Type="Long", Radar="MIM104_M903_PAC3" },
["TorM2 CH"] = { Range=12, Blindspot=1, Height=10, Type="Short", Radar="TorM2" },
["TorM2K CH"] = { Range=12, Blindspot=1, Height=10, Type="Short", Radar="TorM2K" },
["TorM2M CH"] = { Range=16, Blindspot=1, Height=10, Type="Short", Radar="TorM2M" },
["NASAMS3-AMRAAMER CH"] = { Range=50, Blindspot=2, Height=35.7, Type="Medium", Radar="CH_NASAMS3_LN_AMRAAM_ER" },
["NASAMS3-AIM9X2 CH"] = { Range=20, Blindspot=0.2, Height=18, Type="Short", Radar="CH_NASAMS3_LN_AIM9X2" },
["C-RAM CH"] = { Range=2, Blindspot=0, Height=2, Type="Short", Radar="CH_Centurion_C_RAM" },
["PGZ-09 CH"] = { Range=4, Blindspot=0, Height=3, Type="Short", Radar="CH_PGZ09" },
["S350-9M100 CH"] = { Range=15, Blindspot=1.5, Height=8, Type="Short", Radar="CH_S350_50P6_9M100" },
["S350-9M96D CH"] = { Range=150, Blindspot=2.5, Height=30, Type="Long", Radar="CH_S350_50P6_9M96D" },
["LAV-AD CH"] = { Range=8, Blindspot=0.2, Height=4.8, Type="Short", Radar="CH_LAVAD" },
["HQ-22 CH"] = { Range=170, Blindspot=5, Height=27, Type="Long", Radar="CH_HQ22_LN" },
-- units from CH (Military Assets by Currenthill)
-- https://www.currenthill.com/
-- group name MUST contain CHM to ID launcher type correctly!
["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" },
["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" },
["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" },
["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" },
["TorM2K CHM"] = { Range=12, Blindspot=1, Height=10, Type="Short", Radar="TorM2K" },
["TorM2M CHM"] = { Range=16, Blindspot=1, Height=10, Type="Short", Radar="TorM2M" },
["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" },
["C-RAM CHM"] = { Range=2, Blindspot=0, Height=2, Type="Point", Radar="CH_Centurion_C_RAM", Point="true" },
["PGZ-09 CHM"] = { Range=4, Blindspot=0.5, Height=3, Type="Point", Radar="CH_PGZ09", Point="true" },
["S350-9M100 CHM"] = { Range=15, Blindspot=1, Height=8, Type="Short", Radar="CH_S350_50P6_9M100" },
["S350-9M96D CHM"] = { Range=150, Blindspot=2.5, Height=30, Type="Long", Radar="CH_S350_50P6_9M96D" },
["LAV-AD CHM"] = { Range=8, Blindspot=0.16, Height=4.8, Type="Short", Radar="CH_LAVAD" },
["HQ-22 CHM"] = { Range=170, Blindspot=5, Height=27, Type="Long", Radar="CH_HQ22_LN" },
["PGZ-95 CHM"] = { Range=2.5, Blindspot=0.5, Height=2, Type="Point", Radar="CH_PGZ95",Point="true" },
["LD-3000 CHM"] = { Range=2.5, Blindspot=0.1, Height=3, Type="Point", Radar="CH_LD3000_stationary", Point="true" },
["LD-3000M CHM"] = { Range=2.5, Blindspot=0.1, Height=3, Type="Point", Radar="CH_LD3000", Point="true" },
["FlaRakRad CHM"] = { Range=8, Blindspot=1.5, Height=6, Type="Short", Radar="CH_FlaRakRad" },
["IRIS-T SLM CHM"] = { Range=40, Blindspot=0.5, Height=20, Type="Medium", Radar="CH_IRIST_SLM" },
["M903PAC2KAT1 CHM"] = { Range=120, Blindspot=3, Height=24.5, Type="Long", Radar="CH_MIM104_M903_PAC2_KAT1" },
["Skynex CHM"] = { Range=3.5, Blindspot=0.1, Height=3.5, Type="Point", Radar="CH_SkynexHX", Point="true" },
["Skyshield CHM"] = { Range=3.5, Blindspot=0.1, Height=3.5, Type="Point", Radar="CH_Skyshield_Gun", Point="true" },
["WieselOzelot CHM"] = { Range=8, Blindspot=0.16, Height=4.8, Type="Short", Radar="CH_Wiesel2Ozelot" },
["BukM3-9M317M CHM"] = { Range=70, Blindspot=0.25, Height=35, Type="Medium", Radar="CH_BukM3_9A317M" },
["BukM3-9M317MA CHM"] = { Range=70, Blindspot=0.25, Height=35, Type="Medium", Radar="CH_BukM3_9A317MA" },
["SkySabre CHM"] = { Range=30, Blindspot=0.5, Height=10, Type="Medium", Radar="CH_SkySabreLN" },
["Stormer CHM"] = { Range=7.5, Blindspot=0.3, Height=7, Type="Short", Radar="CH_StormerHVM" },
["THAAD CHM"] = { Range=200, Blindspot=40, Height=150, Type="Long", Radar="CH_THAAD_M1120" },
["USInfantryFIM92K CHM"] = { Range=8, Blindspot=0.16, Height=4.8, Type="Short", Radar="CH_USInfantry_FIM92" },
["RBS98M CHM"] = { Range=20, Blindspot=0.2, Height=8, Type="Short", Radar="RBS-98" },
["RBS70 CHM"] = { Range=8, Blindspot=0.25, Height=6, Type="Short", Radar="RBS-70" },
["RBS70M CHM"] = { Range=8, Blindspot=0.25, Height=6, Type="Short", Radar="BV410_RBS70" },
["RBS90 CHM"] = { Range=8, Blindspot=0.25, Height=6, Type="Short", Radar="RBS-90" },
["RBS90M CHM"] = { Range=8, Blindspot=0.25, Height=6, Type="Short", Radar="BV410_RBS90" },
["RBS103A CHM"] = { Range=160, Blindspot=1, Height=36, Type="Long", Radar="LvS-103_Lavett103_Rb103A" },
["RBS103B CHM"] = { Range=120, Blindspot=3, Height=24.5, Type="Long", Radar="LvS-103_Lavett103_Rb103B" },
["RBS103AM CHM"] = { Range=160, Blindspot=1, Height=36, Type="Long", Radar="LvS-103_Lavett103_HX_Rb103A" },
["RBS103BM CHM"] = { Range=120, Blindspot=3, Height=24.5, Type="Long", Radar="LvS-103_Lavett103_HX_Rb103B" },
["Lvkv9040M CHM"] = { Range=2, Blindspot=0.1, Height=1.2, Type="Point", Radar="LvKv9040",Point="true" },
}
-----------------------------------------------------------------------
@@ -520,6 +554,7 @@ do
self.SAM_Table_Long = {}
self.SAM_Table_Medium = {}
self.SAM_Table_Short = {}
self.SAM_Table_PointDef = {}
self.dynamic = dynamic or false
self.checkradius = 25000
self.grouping = 5000
@@ -548,10 +583,6 @@ do
self.SuppressedGroups = {}
-- 0.8 additions
self.automode = true
self.radiusscale = {}
self.radiusscale[MANTIS.SamType.LONG] = 1.1
self.radiusscale[MANTIS.SamType.MEDIUM] = 1.2
self.radiusscale[MANTIS.SamType.SHORT] = 1.3
--self.SAMCheckRanges = {}
self.usezones = false
self.AcceptZones = {}
@@ -560,6 +591,7 @@ do
self.maxlongrange = 1
self.maxmidrange = 2
self.maxshortrange = 2
self.maxpointdefrange = 6
self.maxclassic = 6
self.autoshorad = true
self.ShoradGroupSet = SET_GROUP:New() -- Core.Set#SET_GROUP
@@ -567,7 +599,10 @@ do
self.SkateZones = nil
self.SkateNumber = 3
self.shootandscoot = false
self.shootandscoot = false
self.SmokeDecoy = false
self.SmokeDecoyColor = SMOKECOLOR.White
self.UseEmOnOff = true
if EmOnOff == false then
@@ -579,7 +614,9 @@ do
else
self.advAwacs = false
end
self:SetDLinkCacheTime()
-- Set the string id for output to DCS.log file.
self.lid=string.format("MANTIS %s | ", self.name)
@@ -612,6 +649,8 @@ do
table.insert(self.ewr_templates,awacs)
end
self.logsamstatus = false
self:T({self.ewr_templates})
self.SAM_Group = SET_GROUP:New():FilterPrefixes(self.SAM_Templates_Prefix):FilterCoalitions(self.Coalition)
@@ -638,9 +677,12 @@ do
self.HQ_CC = GROUP:FindByName(self.HQ_Template_CC)
end
-- counter for SAM table updates
self.checkcounter = 1
-- TODO Version
-- @field #string version
self.version="0.8.18"
self.version="0.9.30"
self:I(string.format("***** Starting MANTIS Version %s *****", self.version))
--- FSM Functions ---
@@ -837,7 +879,7 @@ do
self.AcceptZones = AcceptZones or {}
self.RejectZones = RejectZones or {}
self.ConflictZones = ConflictZones or {}
if #AcceptZones > 0 or #RejectZones > 0 or #ConflictZones > 0 then
if #self.AcceptZones > 0 or #self.RejectZones > 0 or #self.ConflictZones > 0 then
self.usezones = true
end
return self
@@ -876,19 +918,31 @@ do
return self
end
--- Function to set Short Range SAMs to spit out smoke as decoy, if an enemy plane is in range.
-- @param #MANTIS self
-- @param #boolean Onoff Set to true for on and nil/false for off.
-- @param #number Color (Optional) Color to use, defaults to `SMOKECOLOR.White`
function MANTIS:SetSmokeDecoy(Onoff,Color)
self.SmokeDecoy = Onoff
self.SmokeDecoyColor = Color or SMOKECOLOR.White
return self
end
--- Function to set number of SAMs going active on a valid, detected thread
-- @param #MANTIS self
-- @param #number Short Number of short-range systems activated, defaults to 1.
-- @param #number Mid Number of mid-range systems activated, defaults to 2.
-- @param #number Long Number of long-range systems activated, defaults to 2.
-- @param #number Classic (non-automode) Number of overall systems activated, defaults to 6.
-- @param #number Point Number of point defense and AAA systems activated, defaults to 6.
-- @return #MANTIS self
function MANTIS:SetMaxActiveSAMs(Short,Mid,Long,Classic)
function MANTIS:SetMaxActiveSAMs(Short,Mid,Long,Classic,Point)
self:T(self.lid .. "SetMaxActiveSAMs")
self.maxclassic = Classic or 6
self.maxlongrange = Long or 1
self.maxmidrange = Mid or 2
self.maxshortrange = Short or 2
self.maxpointdefrange= Point or 6
return self
end
@@ -978,6 +1032,16 @@ do
end
return self
end
--- Function to set how long INTEL DLINK remembers contacts.
-- @param #MANTIS self
-- @param #number seconds Remember this many seconds, at least 5 seconds.
-- @return #MANTIS self
function MANTIS:SetDLinkCacheTime(seconds)
self.DLinkCacheTime = math.abs(seconds or 120)
if self.DLinkCacheTime < 5 then self.DLinkCacheTime = 5 end
return self
end
--- Function to set the detection interval
-- @param #MANTIS self
@@ -1090,6 +1154,24 @@ do
end
return self
end
--- [Internal] Check if any EWR or AWACS is still alive
-- @param #MANTIS self
-- @return #boolean outcome
function MANTIS:_CheckAnyEWRAlive()
self:T(self.lid .. "_CheckAnyEWRAlive")
local alive = false
if self.EWR_Group:CountAlive() > 0 then
alive = true
end
if not alive and self.AWACS_Prefix then
local awacs = GROUP:FindByName(self.AWACS_Prefix)
if awacs and awacs:IsAlive() then
alive = true
end
end
return alive
end
--- [Internal] Function to determine state of the advanced mode
-- @param #MANTIS self
@@ -1264,9 +1346,9 @@ do
-- DEBUG
set = self:_PreFilterHeight(height)
end
local friendlyset -- Core.Set#SET_GROUP
if self.checkforfriendlies == true then
friendlyset = SET_GROUP:New():FilterCoalitions(self.Coalition):FilterCategories({"plane","helicopter"}):FilterFunction(function(grp) if grp and grp:InAir() then return true else return false end end):FilterOnce()
--self.friendlyset -- Core.Set#SET_GROUP
if self.checkforfriendlies == true and self.friendlyset == nil then
self.friendlyset = SET_GROUP:New():FilterCoalitions(self.Coalition):FilterCategories({"plane","helicopter"}):FilterFunction(function(grp) if grp and grp:InAir() then return true else return false end end):FilterStart()
end
for _,_coord in pairs (set) do
local coord = _coord -- get current coord to check
@@ -1282,20 +1364,21 @@ do
zonecheck = self:_CheckCoordinateInZones(coord)
end
if self.verbose and self.debug then
local dectstring = coord:ToStringLLDMS()
local samstring = samcoordinate:ToStringLLDMS()
--local dectstring = coord:ToStringLLDMS()
local samstring = samcoordinate:ToStringMGRS({MGRS_Accuracy=0})
samstring = string.gsub(samstring,"%s","")
local inrange = "false"
if targetdistance <= rad then
inrange = "true"
end
local text = string.format("Checking SAM at %s | Targetdist %d | Rad %d | Inrange %s", samstring, targetdistance, rad, inrange)
local text = string.format("Checking SAM at %s | Tgtdist %.1fkm | Rad %.1fkm | Inrange %s", samstring, targetdistance/1000, rad/1000, inrange)
local m = MESSAGE:New(text,10,"Check"):ToAllIf(self.debug)
self:T(self.lid..text)
end
-- friendlies around?
local nofriendlies = true
if self.checkforfriendlies == true then
local closestfriend, distance = friendlyset:GetClosestGroup(samcoordinate)
local closestfriend, distance = self.friendlyset:GetClosestGroup(samcoordinate)
if closestfriend and distance and distance < rad then
nofriendlies = false
end
@@ -1351,7 +1434,9 @@ do
--IntelTwo:SetClusterRadius(5000)
IntelTwo:Start()
local IntelDlink = INTEL_DLINK:New({IntelOne,IntelTwo},self.name.." DLINK",22,300)
local CacheTime = self.DLinkCacheTime or 120
local IntelDlink = INTEL_DLINK:New({IntelOne,IntelTwo},self.name.." DLINK",22,CacheTime)
IntelDlink:__Start(1)
self:SetUsingDLink(IntelDlink)
@@ -1396,7 +1481,7 @@ do
-- @return #string type Long, medium or short range
-- @return #number blind "blind" spot
function MANTIS:_GetSAMDataFromUnits(grpname,mod,sma,chm)
self:T(self.lid.."_GetSAMRangeFromUnits")
self:T(self.lid.."_GetSAMDataFromUnits")
local found = false
local range = self.checkradius
local height = 3000
@@ -1413,7 +1498,7 @@ do
elseif chm then
SAMData = self.SamDataCH
end
--self:T("Looking to auto-match for "..grpname)
--self:I("Looking to auto-match for "..grpname)
for _,_unit in pairs(units) do
local unit = _unit -- Wrapper.Unit#UNIT
local type = string.lower(unit:GetTypeName())
@@ -1435,6 +1520,17 @@ do
end
if found then break end
end
--- AAA or Point Defense
if not found then
local grp = GROUP:FindByName(grpname)
if (grp and grp:IsAlive() and grp:IsAAA()) or string.find(grpname,"AAA",1,true) then
range = 2000
height = 2000
blind = 50
type = MANTIS.SamType.POINT
found = true
end
end
if not found then
self:E(self.lid .. string.format("*****Could not match radar data for %s! Will default to midrange values!",grpname))
end
@@ -1449,7 +1545,7 @@ do
-- @return #string type Long, medium or short range
-- @return #number blind "blind" spot
function MANTIS:_GetSAMRange(grpname)
self:T(self.lid.."_GetSAMRange")
self:T(self.lid.."_GetSAMRange for "..tostring(grpname))
local range = self.checkradius
local height = 3000
local type = MANTIS.SamType.MEDIUM
@@ -1466,9 +1562,9 @@ do
elseif string.find(grpname,"CHM",1,true) then
CHMod = true
end
if self.automode then
--if self.automode then
for idx,entry in pairs(self.SamData) do
--self:I("ID = " .. idx)
self:T2("ID = " .. idx)
if string.find(grpname,idx,1,true) then
local _entry = entry -- #MANTIS.SamData
type = _entry.Type
@@ -1476,18 +1572,32 @@ do
range = _entry.Range * 1000 * radiusscale -- max firing range
height = _entry.Height * 1000 -- max firing height
blind = _entry.Blindspot
--self:I("Matching Groupname = " .. grpname .. " Range= " .. range)
self:T("Matching Groupname = " .. grpname .. " Range= " .. range)
found = true
break
end
end
--end
--- Secondary - AAA or Point Defense
if not found then
local grp = GROUP:FindByName(grpname)
if (grp and grp:IsAlive() and grp:IsAAA()) or string.find(grpname,"AAA",1,true) then
range = 2000
height = 2000
blind = 50
type = MANTIS.SamType.POINT
found = true
end
end
-- secondary filter if not found
if (not found and self.automode) or HDSmod or SMAMod or CHMod then
--- Tertiary filter if not found
if (not found) or HDSmod or SMAMod or CHMod then
range, height, type = self:_GetSAMDataFromUnits(grpname,HDSmod,SMAMod,CHMod)
elseif not found then
self:E(self.lid .. string.format("*****Could not match radar data for %s! Will default to midrange values!",grpname))
end
if found and string.find(grpname,"SHORAD",1,true) then
type = MANTIS.SamType.POINT -- force short on match
end
return range, height, type, blind
end
@@ -1505,6 +1615,7 @@ do
local SAM_Tbl_lg = {} -- table of long range SAM defense zones
local SAM_Tbl_md = {} -- table of mid range SAM defense zones
local SAM_Tbl_sh = {} -- table of short range SAM defense zones
local SAM_Tbl_pt = {} -- table of point defense/AAA
local SEAD_Grps = {} -- table of SAM names to make evasive
local engagerange = self.engagerange -- firing range in % of max
--cycle through groups and set alarm state etc
@@ -1523,23 +1634,27 @@ do
local grpname = group:GetName()
local grpcoord = group:GetCoordinate()
local grprange,grpheight,type,blind = self:_GetSAMRange(grpname)
table.insert( SAM_Tbl, {grpname, grpcoord, grprange, grpheight, blind})
table.insert( SAM_Tbl, {grpname, grpcoord, grprange, grpheight, blind, type})
--table.insert( SEAD_Grps, grpname )
if type == MANTIS.SamType.LONG then
table.insert( SAM_Tbl_lg, {grpname, grpcoord, grprange, grpheight, blind})
table.insert( SAM_Tbl_lg, {grpname, grpcoord, grprange, grpheight, blind, type})
table.insert( SEAD_Grps, grpname )
--self:T("SAM "..grpname.." is type LONG")
self:T("SAM "..grpname.." is type LONG")
elseif type == MANTIS.SamType.MEDIUM then
table.insert( SAM_Tbl_md, {grpname, grpcoord, grprange, grpheight, blind})
table.insert( SAM_Tbl_md, {grpname, grpcoord, grprange, grpheight, blind, type})
table.insert( SEAD_Grps, grpname )
--self:T("SAM "..grpname.." is type MEDIUM")
self:T("SAM "..grpname.." is type MEDIUM")
elseif type == MANTIS.SamType.SHORT then
table.insert( SAM_Tbl_sh, {grpname, grpcoord, grprange, grpheight, blind})
--self:T("SAM "..grpname.." is type SHORT")
table.insert( SAM_Tbl_sh, {grpname, grpcoord, grprange, grpheight, blind, type})
table.insert( SEAD_Grps, grpname )
self:T("SAM "..grpname.." is type SHORT")
elseif type == MANTIS.SamType.POINT then
table.insert( SAM_Tbl_pt, {grpname, grpcoord, grprange, grpheight, blind, type})
self:T("SAM "..grpname.." is type POINT")
self.ShoradGroupSet:Add(grpname,group)
if not self.autoshorad then
table.insert( SEAD_Grps, grpname )
end
end
end
self.SamStateTracker[grpname] = "GREEN"
end
@@ -1548,6 +1663,7 @@ do
self.SAM_Table_Long = SAM_Tbl_lg
self.SAM_Table_Medium = SAM_Tbl_md
self.SAM_Table_Short = SAM_Tbl_sh
self.SAM_Table_PointDef = SAM_Tbl_pt
-- make SAMs evasive
local mysead = SEAD:New( SEAD_Grps, self.Padding ) -- Functional.Sead#SEAD
mysead:SetEngagementRange(engagerange)
@@ -1571,7 +1687,8 @@ do
local SAM_Tbl = {} -- table of SAM defense zones
local SAM_Tbl_lg = {} -- table of long range SAM defense zones
local SAM_Tbl_md = {} -- table of mid range SAM defense zones
local SAM_Tbl_sh = {} -- table of short range SAM defense zon
local SAM_Tbl_sh = {} -- table of short range SAM defense zones
local SAM_Tbl_pt = {} -- table of point defense/AAA
local SEAD_Grps = {} -- table of SAM names to make evasive
local engagerange = self.engagerange -- firing range in % of max
--cycle through groups and set alarm state etc
@@ -1582,17 +1699,23 @@ do
local grpname = group:GetName()
local grpcoord = group:GetCoordinate()
local grprange, grpheight,type,blind = self:_GetSAMRange(grpname)
table.insert( SAM_Tbl, {grpname, grpcoord, grprange, grpheight, blind}) -- make the table lighter, as I don't really use the zone here
-- TODO the below might stop working at some point after some hours, needs testing
--local radaralive = group:IsSAM()
local radaralive = true
table.insert( SAM_Tbl, {grpname, grpcoord, grprange, grpheight, blind, type}) -- make the table lighter, as I don't really use the zone here
table.insert( SEAD_Grps, grpname )
if type == MANTIS.SamType.LONG then
table.insert( SAM_Tbl_lg, {grpname, grpcoord, grprange, grpheight, blind})
--self:I({grpname,grprange, grpheight})
elseif type == MANTIS.SamType.MEDIUM then
table.insert( SAM_Tbl_md, {grpname, grpcoord, grprange, grpheight, blind})
--self:I({grpname,grprange, grpheight})
elseif type == MANTIS.SamType.SHORT then
table.insert( SAM_Tbl_sh, {grpname, grpcoord, grprange, grpheight, blind})
-- self:I({grpname,grprange, grpheight})
if type == MANTIS.SamType.LONG and radaralive then
table.insert( SAM_Tbl_lg, {grpname, grpcoord, grprange, grpheight, blind, type})
self:T({grpname,grprange, grpheight})
elseif type == MANTIS.SamType.MEDIUM and radaralive then
table.insert( SAM_Tbl_md, {grpname, grpcoord, grprange, grpheight, blind, type})
self:T({grpname,grprange, grpheight})
elseif type == MANTIS.SamType.SHORT and radaralive then
table.insert( SAM_Tbl_sh, {grpname, grpcoord, grprange, grpheight, blind, type})
self:T({grpname,grprange, grpheight})
elseif type == MANTIS.SamType.POINT or (not radaralive) then
table.insert( SAM_Tbl_pt, {grpname, grpcoord, grprange, grpheight, blind, type})
self:T({grpname,grprange, grpheight})
self.ShoradGroupSet:Add(grpname,group)
if self.autoshorad then
self.Shorad.Groupset = self.ShoradGroupSet
@@ -1604,6 +1727,7 @@ do
self.SAM_Table_Long = SAM_Tbl_lg
self.SAM_Table_Medium = SAM_Tbl_md
self.SAM_Table_Short = SAM_Tbl_sh
self.SAM_Table_PointDef = SAM_Tbl_pt
-- make SAMs evasive
if self.mysead ~= nil then
local mysead = self.mysead
@@ -1647,21 +1771,34 @@ do
-- @param #table detset Table of COORDINATES
-- @param #boolean dlink Using DLINK
-- @param #number limit of SAM sites to go active on a contact
-- @return #MANTIS self
-- @return #number instatusred
-- @return #number instatusgreen
-- @return #number activeshorads
function MANTIS:_CheckLoop(samset,detset,dlink,limit)
self:T(self.lid .. "CheckLoop " .. #detset .. " Coordinates")
local switchedon = 0
local instatusred = 0
local instatusgreen = 0
local activeshorads = 0
local SEADactive = 0
for _,_data in pairs (samset) do
local samcoordinate = _data[2]
local name = _data[1]
local radius = _data[3]
local height = _data[4]
local blind = _data[5] * 1.25 + 1
local shortsam = (_data[6] == MANTIS.SamType.SHORT) and true or false
if not shortsam then
shortsam = (_data[6] == MANTIS.SamType.POINT) and true or false
end
local samgroup = GROUP:FindByName(name)
local IsInZone, Distance = self:_CheckObjectInZone(detset, samcoordinate, radius, height, dlink)
local suppressed = self.SuppressedGroups[name] or false
local activeshorad = self.Shorad.ActiveGroups[name] or false
if IsInZone and not suppressed and not activeshorad then --check any target in zone and not currently managed by SEAD
local activeshorad = false
if self.Shorad and self.Shorad.ActiveGroups and self.Shorad.ActiveGroups[name] then
activeshorad = true
end
if IsInZone and (not suppressed) and (not activeshorad) then --check any target in zone and not currently managed by SEAD
if samgroup:IsAlive() then
-- switch on SAM
local switch = false
@@ -1673,12 +1810,23 @@ do
elseif (not self.UseEmOnOff) and switchedon < limit then
samgroup:OptionAlarmStateRed()
switchedon = switchedon + 1
switch = true
switch = true
end
if self.SamStateTracker[name] ~= "RED" and switch then
self:__RedState(1,samgroup)
self.SamStateTracker[name] = "RED"
end
-- TODO doesn't work
if shortsam == true and self.SmokeDecoy == true then
self:T("Smoking")
local units = samgroup:GetUnits() or {}
local smoke = self.SmokeDecoyColor or SMOKECOLOR.White
for _,unit in pairs(units) do
if unit and unit:IsAlive() then
unit:GetCoordinate():Smoke(smoke)
end
end
end
-- link in to SHORAD if available
-- DONE: Test integration fully
if self.ShoradLink and (Distance < self.ShoradActDistance or Distance < blind ) then -- don't give SHORAD position away too early
@@ -1691,7 +1839,7 @@ do
-- debug output
if (self.debug or self.verbose) and switch then
local text = string.format("SAM %s in alarm state RED!", name)
local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
--local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug
if self.verbose then self:I(self.lid..text) end
end
end --end alive
@@ -1709,41 +1857,81 @@ do
end
if self.debug or self.verbose then
local text = string.format("SAM %s in alarm state GREEN!", name)
local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
--local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
if self.verbose then self:I(self.lid..text) end
end
end --end alive
end --end check
end --for for loop
return self
end --end check
end --for loop
if self.debug or self.verbose or self.logsamstatus then
for _,_status in pairs(self.SamStateTracker) do
if _status == "GREEN" then
instatusgreen=instatusgreen+1
elseif _status == "RED" then
instatusred=instatusred+1
end
end
if self.Shorad then
for _,_name in pairs(self.Shorad.ActiveGroups or {}) do
activeshorads=activeshorads+1
end
end
end
return instatusred, instatusgreen, activeshorads
end
--- [Internal] Check detection function
-- @param #MANTIS self
-- @param Functional.Detection#DETECTION_AREAS detection Detection object
-- @param #boolean dlink
-- @param #boolean reporttolog
-- @return #MANTIS self
function MANTIS:_Check(detection,dlink)
function MANTIS:_Check(detection,dlink,reporttolog)
self:T(self.lid .. "Check")
--get detected set
local detset = detection:GetDetectedItemCoordinates()
--self:T("Check:", {detset})
-- randomly update SAM Table
local rand = math.random(1,100)
if rand > 65 then -- 1/3 of cases
-- update SAM Table evey 3 runs
if self.checkcounter%3 == 0 then
self:_RefreshSAMTable()
end
self.checkcounter = self.checkcounter + 1
local instatusred = 0
local instatusgreen = 0
local activeshorads = 0
-- switch SAMs on/off if (n)one of the detected groups is inside their reach
if self.automode then
local samset = self.SAM_Table_Long -- table of i.1=names, i.2=coordinates, i.3=firing range, i.4=firing height
self:_CheckLoop(samset,detset,dlink,self.maxlongrange)
local instatusredl, instatusgreenl, activeshoradsl = self:_CheckLoop(samset,detset,dlink,self.maxlongrange)
local samset = self.SAM_Table_Medium -- table of i.1=names, i.2=coordinates, i.3=firing range, i.4=firing height
self:_CheckLoop(samset,detset,dlink,self.maxmidrange)
local instatusredm, instatusgreenm, activeshoradsm = self:_CheckLoop(samset,detset,dlink,self.maxmidrange)
local samset = self.SAM_Table_Short -- table of i.1=names, i.2=coordinates, i.3=firing range, i.4=firing height
self:_CheckLoop(samset,detset,dlink,self.maxshortrange)
local instatusreds, instatusgreens, activeshoradss = self:_CheckLoop(samset,detset,dlink,self.maxshortrange)
local samset = self.SAM_Table_PointDef -- table of i.1=names, i.2=coordinates, i.3=firing range, i.4=firing height
instatusred, instatusgreen, activeshorads = self:_CheckLoop(samset,detset,dlink,self.maxpointdefrange)
else
local samset = self:_GetSAMTable() -- table of i.1=names, i.2=coordinates, i.3=firing range, i.4=firing height
self:_CheckLoop(samset,detset,dlink,self.maxclassic)
instatusred, instatusgreen, activeshorads = self:_CheckLoop(samset,detset,dlink,self.maxclassic)
end
local function GetReport()
local statusreport = REPORT:New("\nMANTIS Status "..self.name)
statusreport:Add("+-----------------------------+")
statusreport:Add(string.format("+ SAM in RED State: %2d",instatusred))
statusreport:Add(string.format("+ SAM in GREEN State: %2d",instatusgreen))
if self.Shorad then
statusreport:Add(string.format("+ SHORAD active: %2d",activeshorads))
end
statusreport:Add("+-----------------------------+")
return statusreport
end
if self.debug or self.verbose then
local statusreport = GetReport()
MESSAGE:New(statusreport:Text(),10):ToAll():ToLog()
elseif reporttolog == true then
local statusreport = GetReport()
MESSAGE:New(statusreport:Text(),10):ToLog()
end
return self
end
@@ -1828,7 +2016,7 @@ do
end
--]]
if self.autoshorad then
self.Shorad = SHORAD:New(self.name.."-SHORAD",self.name.."-SHORAD",self.SAM_Group,self.ShoradActDistance,self.ShoradTime,self.coalition,self.UseEmOnOff)
self.Shorad = SHORAD:New(self.name.."-SHORAD","SHORAD",self.SAM_Group,self.ShoradActDistance,self.ShoradTime,self.coalition,self.UseEmOnOff)
self.Shorad:SetDefenseLimits(80,95)
self.ShoradLink = true
self.Shorad.Groupset=self.ShoradGroupSet
@@ -1851,14 +2039,36 @@ do
self:T({From, Event, To})
-- check detection
if not self.state2flag then
self:_Check(self.Detection,self.DLink)
self:_Check(self.Detection,self.DLink,self.logsamstatus)
end
--[[ check Awacs
if self.advAwacs and not self.state2flag then
self:_Check(self.AWACS_Detection,false)
local EWRAlive = self:_CheckAnyEWRAlive()
local function FindSAMSRTR()
for i=1,1000 do
local randomsam = self.SAM_Group:GetRandom()
if randomsam and randomsam:IsAlive() then
if randomsam:IsSAM() then return randomsam end
end
end
end
-- Switch on a random SR/TR if no EWR left over
if not EWRAlive then
local randomsam = FindSAMSRTR() -- Wrapper.Group#GROUP
if randomsam and randomsam:IsAlive() then
if self.UseEmOnOff then
randomsam:EnableEmission(true)
else
randomsam:OptionAlarmStateRed()
end
local name = randomsam:GetName()
if self.SamStateTracker[name] ~= "RED" then
self:__RedState(1,randomsam)
self.SamStateTracker[name] = "RED"
end
end
end
--]]
-- relocate HQ and EWR
if self.autorelocate then
@@ -1868,8 +2078,6 @@ do
local halfintv = math.floor(timepassed / relointerval)
--self:T({timepassed=timepassed, halfintv=halfintv})
if halfintv >= 1 then
self.TimeStamp = timer.getAbsTime()
self:_Relocate()
@@ -1998,7 +2206,7 @@ do
local Shorad = self.Shorad
local radius = self.checkradius
local ontime = self.ShoradTime
Shorad:WakeUpShorad(Name, radius, ontime)
Shorad:WakeUpShorad(Name, radius, ontime, nil, true)
self:__ShoradActivated(1,Name, radius, ontime)
end
return self

View File

@@ -53,6 +53,8 @@
--
-- # Developer Note
--
-- ![Banner Image](..\Images\deprecated.png)
--
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE.
-- Therefore, this class is considered to be deprecated and superseded by the [Functional.Fox](https://flightcontrol-master.github.io/MOOSE_DOCS_DEVELOP/Documentation/Functional.Fox.html) class, which provides the same functionality.
--

View File

@@ -3814,15 +3814,20 @@ function RAT:Status(message, forID)
local N0units=group:GetInitialSize()
-- Monitor travelled distance since last check.
local Pnow=coords
local Dtravel=Pnow:Get2DDistance(ratcraft.Pnow)
ratcraft.Pnow=Pnow
local Dtravel=0
if coords and ratcraft.Pnow then
local Dtravel=coords:Get2DDistance(ratcraft.Pnow)
ratcraft.Pnow=coords
end
-- Add up the travelled distance.
ratcraft.Distance=ratcraft.Distance+Dtravel
-- Distance remaining to destination.
local Ddestination=Pnow:Get2DDistance(ratcraft.destination:GetCoordinate())
local Ddestination=-1
if ratcraft.Pnow then
Ddestination=ratcraft.Pnow:Get2DDistance(ratcraft.destination:GetCoordinate())
end
-- Status report.
if (forID and spawnindex==forID) or (not forID) then

View File

@@ -107,6 +107,9 @@
-- @field Sound.SRS#MSRSQUEUE instructsrsQ SRS queue for range instructor.
-- @field #number Coalition Coalition side for the menu, if any.
-- @field Core.Menu#MENU_MISSION menuF10root Specific user defined root F10 menu.
-- @field #number ceilingaltitude Range ceiling altitude in ft MSL. Aircraft above this altitude are not considered to be in the range. Default is 20000 ft.
-- @field #boolean ceilingenabled Range has a ceiling and is not unlimited. Default is false.
-- @extends Core.Fsm#FSM
--- *Don't only practice your art, but force your way into its secrets; art deserves that, for it and knowledge can raise man to the Divine.* - Ludwig van Beethoven
@@ -273,6 +276,10 @@
-- -- Create a range object.
-- GoldwaterRange=RANGE:New("Goldwater Range")
--
-- -- Set and enable the range ceiling altitude in feet MSL. If aircraft are above this altitude they are not considered to be in the range.
-- GoldwaterRange:SetRangeCeiling(20000)
-- GoldwaterRange:EnableRangeCeiling(true)
--
-- -- Distance between strafe target and foul line. You have to specify the names of the unit or static objects.
-- -- Note that this could also be done manually by simply measuring the distance between the target and the foul line in the ME.
-- GoldwaterRange:GetFoullineDistance("GWR Strafe Pit Left 1", "GWR Foul Line Left")
@@ -358,6 +365,8 @@ RANGE = {
targetpath = nil,
targetprefix = nil,
Coalition = nil,
ceilingaltitude = 20000,
ceilingenabled = false,
}
--- Default range parameters.
@@ -594,7 +603,7 @@ RANGE.MenuF10Root = nil
--- Range script version.
-- @field #string version
RANGE.version = "2.8.0"
RANGE.version = "2.8.1"
-- TODO list:
-- TODO: Verbosity level for messages.
@@ -1085,6 +1094,37 @@ function RANGE:SetRangeZone( zone )
return self
end
--- Set range ceiling altitude in feet MSL.
-- @param #RANGE self
-- @param #number altitude (optional) Ceiling altitude of the range in ft MSL. Default 20000ft MSL
-- @return #RANGE self
function RANGE:SetRangeCeiling( altitude )
self:T(self.lid.."SetRangeCeiling")
if altitude and type(altitude) == "number" then
self.ceilingaltitude=altitude
else
self:E(self.lid.."Altitude either not provided or is not a number, using default setting (20000).")
self.ceilingaltitude=20000
end
return self
end
--- Enable range ceiling. Aircraft must be below the ceiling altitude to be considered in the range zone.
-- @param #RANGE self
-- @param #boolean enabled True if you would like to enable the ceiling check. If no value give, will Default to false.
-- @return #RANGE self
function RANGE:EnableRangeCeiling( enabled )
self:T(self.lid.."EnableRangeCeiling")
if enabled and type(enabled) == "boolean" then
self.ceilingenabled=enabled
else
self:E(self.lid.."Enabled either not provide or is not a boolean, using default setting (false).")
self.ceilingenabled=false
end
return self
end
--- Set smoke color for marking bomb targets. By default bomb targets are marked by red smoke.
-- @param #RANGE self
-- @param Utilities.Utils#SMOKECOLOR colorid Color id. Default `SMOKECOLOR.Red`.
@@ -1893,7 +1933,7 @@ function RANGE:OnEventHit( EventData )
local _currentTarget = self.strafeStatus[_unitID] --#RANGE.StrafeStatus
-- Player has rolled in on a strafing target.
if _currentTarget and target:IsAlive() then
if _currentTarget and target and target:IsAlive() then
local playerPos = _unit:GetCoordinate()
local targetPos = target:GetCoordinate()
@@ -1992,10 +2032,10 @@ function RANGE._OnImpact(weapon, self, playerData, attackHdg, attackAlt, attackV
-- Smoke impact point of bomb.
if playerData and playerData.smokebombimpact and insidezone then
if playerData and playerData.delaysmoke then
timer.scheduleFunction( self._DelayedSmoke, { coord = impactcoord, color = playerData.smokecolor }, timer.getTime() + self.TdelaySmoke )
if playerData.delaysmoke then
impactcoord:Smoke(playerData.smokecolor, 30, self.TdelaySmoke)
else
impactcoord:Smoke( playerData.smokecolor )
impactcoord:Smoke(playerData.smokecolor, 30)
end
end
@@ -2062,7 +2102,12 @@ function RANGE._OnImpact(weapon, self, playerData, attackHdg, attackAlt, attackV
result.attackHdg = attackHdg
result.attackVel = attackVel
result.attackAlt = attackAlt
result.date=os and os.date() or "n/a"
if os and os.date then
result.date=os.date()
else
self:E(self.lid.."os or os.date() not available")
result.date = "n/a"
end
-- Add to table.
table.insert( _results, result )
@@ -2595,13 +2640,6 @@ end
-- Display Messages
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Start smoking a coordinate with a delay.
-- @param #table _args Argements passed.
function RANGE._DelayedSmoke( _args )
_args.coord:Smoke(_args.color)
--trigger.action.smoke( _args.coord:GetVec3(), _args.color )
end
--- Display top 10 stafing results of a specific player.
-- @param #RANGE self
-- @param #string _unitName Name of the player unit.
@@ -2987,7 +3025,7 @@ function RANGE:_DisplayBombTargets( _unitname )
end
end
self:_DisplayMessageToGroup( _unit, _text, 120, true, true, _multiplayer )
self:_DisplayMessageToGroup( _unit, _text, 150, true, true, _multiplayer )
end
end
@@ -3112,7 +3150,10 @@ function RANGE:_CheckPlayers()
if unit and unit:IsAlive() then
if unit:IsInZone( self.rangezone ) then
local unitalt = unit:GetAltitude(false)
local unitaltinfeet = UTILS.MetersToFeet(unitalt)
if unit:IsInZone(self.rangezone) and (not self.ceilingenabled or unitaltinfeet < self.ceilingaltitude) then
------------------------------
-- Player INSIDE Range Zone --
@@ -3453,10 +3494,10 @@ function RANGE:_AddF10Commands( _unitName )
-- Range menu
local _rangePath = MENU_GROUP:New( group, self.rangename, _rootMenu )
local _statsPath = MENU_GROUP:New( group, "Statistics", _rangePath )
local _markPath = MENU_GROUP:New( group, "Mark Targets", _rangePath )
local _settingsPath = MENU_GROUP:New( group, "My Settings", _rangePath )
local _infoPath = MENU_GROUP:New( group, "Range Info", _rangePath )
local _markPath = MENU_GROUP:New( group, "Mark Targets", _rangePath )
local _statsPath = MENU_GROUP:New( group, "Statistics", _rangePath )
local _settingsPath = MENU_GROUP:New( group, "My Settings", _rangePath )
-- F10/On the Range/<Range Name>/My Settings/
local _mysmokePath = MENU_GROUP:New( group, "Smoke Color", _settingsPath )

View File

@@ -19,7 +19,7 @@
--
-- ### Authors: **applevangelist**, **FlightControl**
--
-- Last Update: Dec 2023
-- Last Update: Dec 2024
--
-- ===
--
@@ -28,6 +28,16 @@
---
-- @type SEAD
-- @field #string ClassName The Class Name.
-- @field #table TargetSkill Table of target skills.
-- @field #table SEADGroupPrefixes Table of SEAD prefixes.
-- @field #table SuppressedGroups Table of currently suppressed groups.
-- @field #number EngagementRange Engagement Range.
-- @field #number Padding Padding in seconds.
-- @field #function CallBack Callback function for suppression plans.
-- @field #boolean UseCallBack Switch for callback function to be used.
-- @field #boolean debug Debug switch.
-- @field #boolen WeaponTrack Track switch, if true track weapon speed for 30 secs.
-- @extends Core.Base#BASE
--- Make SAM sites execute evasive and defensive behaviour when being fired upon.
@@ -56,10 +66,11 @@ SEAD = {
SEADGroupPrefixes = {},
SuppressedGroups = {},
EngagementRange = 75, -- default 75% engagement range Feature Request #1355
Padding = 10,
Padding = 15,
CallBack = nil,
UseCallBack = false,
debug = false,
WeaponTrack = false,
}
--- Missile enumerators
@@ -69,6 +80,7 @@ SEAD = {
["AGM_122"] = "AGM_122",
["AGM_84"] = "AGM_84",
["AGM_45"] = "AGM_45",
["AGM_65"] = "AGM_65",
["ALARM"] = "ALARM",
["LD-10"] = "LD-10",
["X_58"] = "X_58",
@@ -88,6 +100,7 @@ SEAD = {
-- km and mach
["AGM_88"] = { 150, 3},
["AGM_45"] = { 12, 2},
["AGM_65"] = { 16, 0.9},
["AGM_122"] = { 16.5, 2.3},
["AGM_84"] = { 280, 0.8},
["ALARM"] = { 45, 2},
@@ -144,7 +157,7 @@ function SEAD:New( SEADGroupPrefixes, Padding )
self:AddTransition("*", "ManageEvasion", "*")
self:AddTransition("*", "CalculateHitZone", "*")
self:I("*** SEAD - Started Version 0.4.6")
self:I("*** SEAD - Started Version 0.4.9")
return self
end
@@ -371,7 +384,7 @@ function SEAD:onafterManageEvasion(From,Event,To,_targetskill,_targetgroup,SEADP
reach = wpndata[1] * 1.1
local mach = wpndata[2]
wpnspeed = math.floor(mach * 340.29)
if Weapon then
if Weapon and Weapon:GetSpeed() > 0 then
wpnspeed = Weapon:GetSpeed()
self:T(string.format("*** SEAD - Weapon Speed from WEAPON: %f m/s",wpnspeed))
end
@@ -452,29 +465,38 @@ end
-- @return #SEAD self
function SEAD:HandleEventShot( EventData )
self:T( { EventData.id } )
local SEADPlane = EventData.IniUnit -- Wrapper.Unit#UNIT
local SEADGroup = EventData.IniGroup -- Wrapper.Group#GROUP
local SEADPlanePos = SEADPlane:GetCoordinate() -- Core.Point#COORDINATE
local SEADUnit = EventData.IniDCSUnit
local SEADUnitName = EventData.IniDCSUnitName
local SEADWeapon = EventData.Weapon -- Identify the weapon fired
local SEADWeaponName = EventData.WeaponName -- return weapon type
local WeaponWrapper = WEAPON:New(EventData.Weapon)
--local SEADWeaponSpeed = WeaponWrapper:GetSpeed() -- mps
self:T( "*** SEAD - Missile Launched = " .. SEADWeaponName)
--self:T({ SEADWeapon })
local SEADWeapon = EventData.Weapon -- Identify the weapon fired
local SEADWeaponName = EventData.WeaponName or "None" -- return weapon type
if self:_CheckHarms(SEADWeaponName) then
--UTILS.PrintTableToLog(EventData)
local SEADPlane = EventData.IniUnit -- Wrapper.Unit#UNIT
if not SEADPlane then return self end -- case IniUnit is empty
local SEADGroup = EventData.IniGroup -- Wrapper.Group#GROUP
local SEADPlanePos = SEADPlane:GetCoordinate() -- Core.Point#COORDINATE
local SEADUnit = EventData.IniDCSUnit
local SEADUnitName = EventData.IniDCSUnitName
local WeaponWrapper = WEAPON:New(EventData.Weapon) -- Wrapper.Weapon#WEAPON
self:T( "*** SEAD - Missile Launched = " .. SEADWeaponName)
self:T( '*** SEAD - Weapon Match' )
if self.WeaponTrack == true then
WeaponWrapper:SetFuncTrack(function(weapon) env.info(string.format("*** Weapon Speed: %d m/s",weapon:GetSpeed() or -1)) end)
WeaponWrapper:StartTrack(0.1)
WeaponWrapper:StopTrack(30)
end
local _targetskill = "Random"
local _targetgroupname = "none"
local _target = EventData.Weapon:getTarget() -- Identify target
if not _target or self.debug then -- AGM-88 or 154 w/o target data
self:E("***** SEAD - No target data for " .. (SEADWeaponName or "None"))
if string.find(SEADWeaponName,"AGM_88",1,true) or string.find(SEADWeaponName,"AGM_154",1,true) then
self:I("**** Tracking AGM-88/154 with no target data.")
self:T("**** Tracking AGM-88/154 with no target data.")
local pos0 = SEADPlane:GetCoordinate()
local fheight = SEADPlane:GetHeight()
self:__CalculateHitZone(20,SEADWeapon,pos0,fheight,SEADGroup,SEADWeaponName)
@@ -520,7 +542,7 @@ function SEAD:HandleEventShot( EventData )
end
if SEADGroupFound == true then -- yes we are being attacked
if string.find(SEADWeaponName,"ADM_141",1,true) then
self:__ManageEvasion(2,_targetskill,_targetgroup,SEADPlanePos,SEADWeaponName,SEADGroup,0,WeaponWrapper)
self:__ManageEvasion(2,_targetskill,_targetgroup,SEADPlanePos,SEADWeaponName,SEADGroup,2,WeaponWrapper)
else
self:ManageEvasion(_targetskill,_targetgroup,SEADPlanePos,SEADWeaponName,SEADGroup,0,WeaponWrapper)
end

View File

@@ -21,7 +21,7 @@
-- @image Functional.Shorad.jpg
--
-- Date: Nov 2021
-- Last Update: Nov 2023
-- Last Update: Jan 2025
-------------------------------------------------------------------------
--- **SHORAD** class, extends Core.Base#BASE
@@ -113,7 +113,7 @@ SHORAD = {
SkateNumber = 3,
SkateZones = nil,
minscootdist = 100,
minscootdist = 3000,
maxscootdist = 3000,
scootrandomcoord = false,
}
@@ -443,7 +443,9 @@ do
for _,_groups in pairs (shoradset) do
local groupname = _groups:GetName()
if string.find(groupname, tgtgrp, 1, true) then
returnname = true
if _groups:IsSAM() then
returnname = true
end
end
end
return returnname
@@ -470,6 +472,7 @@ do
-- @param #number Radius Radius of the #ZONE
-- @param #number ActiveTimer Number of seconds to stay active
-- @param #number TargetCat (optional) Category, i.e. Object.Category.UNIT or Object.Category.STATIC
-- @param #boolean ShotAt If true, function is called after a shot
-- @return #SHORAD self
-- @usage Use this function to integrate with other systems, example
--
@@ -479,7 +482,7 @@ do
-- mymantis = MANTIS:New("BlueMantis","Blue SAM","Blue EWR",nil,"blue",false,"Blue Awacs")
-- mymantis:AddShorad(myshorad,720)
-- mymantis:Start()
function SHORAD:onafterWakeUpShorad(From, Event, To, TargetGroup, Radius, ActiveTimer, TargetCat)
function SHORAD:onafterWakeUpShorad(From, Event, To, TargetGroup, Radius, ActiveTimer, TargetCat, ShotAt)
self:T(self.lid .. " WakeUpShorad")
self:T({TargetGroup, Radius, ActiveTimer, TargetCat})
local targetcat = TargetCat or Object.Category.UNIT
@@ -521,7 +524,27 @@ do
-- go through set and find the one(s) to activate
local TDiff = 4
for _,_group in pairs (shoradset) do
if _group:IsAnyInZone(targetzone) then
local groupname = _group:GetName()
if groupname == TargetGroup and ShotAt==true then
-- Shot at a SHORAD group
if self.UseEmOnOff then
_group:EnableEmission(false)
end
_group:OptionAlarmStateGreen()
self.ActiveGroups[groupname] = nil
local text = string.format("Shot at SHORAD %s! Evading!", _group:GetName())
self:T(text)
local m = MESSAGE:New(text,10,"SHORAD"):ToAllIf(self.debug)
--Shoot and Scoot
if self.shootandscoot then
self:__ShootAndScoot(1,_group)
end
elseif _group:IsAnyInZone(targetzone) or groupname == TargetGroup then
-- shot at a group we protect
local text = string.format("Waking up SHORAD %s", _group:GetName())
self:T(text)
local m = MESSAGE:New(text,10,"SHORAD"):ToAllIf(self.debug)
@@ -529,7 +552,6 @@ do
_group:EnableEmission(true)
end
_group:OptionAlarmStateRed()
local groupname = _group:GetName()
if self.ActiveGroups[groupname] == nil then -- no timer yet for this group
self.ActiveGroups[groupname] = { Timing = ActiveTimer }
local endtime = timer.getTime() + (ActiveTimer * math.random(75,100) / 100 ) -- randomize wakeup a bit
@@ -607,7 +629,7 @@ do
_targetgroupname = tgtgrp:GetName() -- group name
_targetskill = tgtgrp:GetUnit(1):GetSkill()
self:T("*** Found Target = ".. _targetgroupname)
self:WakeUpShorad(_targetgroupname, self.Radius, self.ActiveTimer, Object.Category.UNIT)
self:WakeUpShorad(_targetgroupname, self.Radius, self.ActiveTimer, Object.Category.UNIT,true)
end
end
end
@@ -736,7 +758,7 @@ do
-- if being shot at, find closest SHORADs to activate
if shotatsams or shotatus then
self:T({shotatsams=shotatsams,shotatus=shotatus})
self:WakeUpShorad(targetgroupname, self.Radius, self.ActiveTimer, targetcat)
self:WakeUpShorad(targetgroupname, self.Radius, self.ActiveTimer, targetcat, true)
end
end
end

View File

@@ -27,13 +27,13 @@
-- @module Functional.Tiresias
-- @image Functional.Tiresias.jpg
--
-- Last Update: Dec 2023
-- Last Update: Jan 2024
-------------------------------------------------------------------------
--- **TIRESIAS** class, extends Core.Base#BASE
-- @type TIRESIAS
-- @field #string ClassName
-- @field #booelan debug
-- @field #boolean debug
-- @field #string version
-- @field #number Interval
-- @field Core.Set#SET_GROUP GroundSet
@@ -47,7 +47,8 @@
-- @field #number HeloSwitchRange
-- @field #number PlaneSwitchRange
-- @field Core.Set#SET_GROUP FlightSet
-- @field #boolean SwitchAAA
-- @field #boolean SwitchAAA
-- @field #boolean SwitchSAM
-- @extends Core.Fsm#FSM
---
@@ -97,7 +98,7 @@
TIRESIAS = {
ClassName = "TIRESIAS",
debug = false,
version = "0.0.5",
version = "0.0.6",
Interval = 20,
GroundSet = nil,
VehicleSet = nil,
@@ -107,7 +108,10 @@ TIRESIAS = {
AAARange = 60, -- 60%
HeloSwitchRange = 10, -- NM
PlaneSwitchRange = 25, -- NM
SAMSwitchRange = 75, --NM
SwitchAAA = true,
SwitchSAM = false,
MantisHandlingSAM = true
}
--- [USER] Create a new Tiresias object and start it up.
@@ -129,7 +133,7 @@ function TIRESIAS:New()
self:AddTransition("*", "Status", "*") -- TIRESIAS status update.
self:AddTransition("*", "Stop", "Stopped") -- Stop FSM.
self.ExceptionSet = SET_GROUP:New():Clear(false)
--self.ExceptionSet = SET_GROUP:New():Clear(false)
self:HandleEvent(EVENTS.PlayerEnterAircraft,self._EventHandler)
@@ -169,10 +173,12 @@ end
-- @param #TIRESIAS self
-- @param #number HeloMiles Radius around a Helicopter in which AI ground units will be activated. Defaults to 10NM.
-- @param #number PlaneMiles Radius around an Airplane in which AI ground units will be activated. Defaults to 25NM.
-- @param #number SAMMiles Radius around an Airplane in which SAM AI ground units will be activated. Defaults to 75NM.
-- @return #TIRESIAS self
function TIRESIAS:SetActivationRanges(HeloMiles,PlaneMiles)
function TIRESIAS:SetActivationRanges(HeloMiles,PlaneMiles,SAMMiles)
self.HeloSwitchRange = HeloMiles or 10
self.PlaneSwitchRange = PlaneMiles or 25
self.SAMSwitchRange = SAMMiles or 75
return self
end
@@ -187,12 +193,28 @@ function TIRESIAS:SetAAARanges(FiringRange,SwitchAAA)
return self
end
--- [USER] Set if TIRESIAS is also taking care or SAM installtions.
-- @param #TIRESIAS self
-- @param #boolean OnOff Set to true to switch on, false to switch off. Default is false.
-- @param #boolean MantisPresent Set to true to switch on, false to switch off. Default is true.
-- @return #TIRESIAS self
function TIRESIAS:SetSwitchSAM(OnOff,MantisPresent)
if OnOff == nil or OnOff == false then
self.SwitchSAM = false
else
self.SwitchSAM = true
end
self.MantisHandlingSAM = MantisPresent or true
return self
end
--- [USER] Add a SET_GROUP of GROUP objects as exceptions. Can be done multiple times. Does **not** work work for GROUP objects spawned into the SET after start, i.e. the groups need to exist in the game already.
-- @param #TIRESIAS self
-- @param Core.Set#SET_GROUP Set to add to the exception list.
-- @return #TIRESIAS self
function TIRESIAS:AddExceptionSet(Set)
self:T(self.lid.."AddExceptionSet")
if not self.ExceptionSet then self.ExceptionSet = SET_GROUP:New():Clear(false) end
local exceptions = self.ExceptionSet
Set:ForEachGroupAlive(
function(grp)
@@ -242,9 +264,9 @@ function TIRESIAS._FilterAAA(Group)
local grp = Group -- Wrapper.Group#GROUP
local isaaa = grp:IsAAA()
if isaaa == true and grp:IsGround() and not grp:IsShip() then
return true -- remove from SET
return true -- keep in SET
else
return false -- keep in SET
return false -- remove from SET
end
end
@@ -255,9 +277,9 @@ function TIRESIAS._FilterSAM(Group)
local grp = Group -- Wrapper.Group#GROUP
local issam = grp:IsSAM()
if issam == true and grp:IsGround() and not grp:IsShip() then
return true -- remove from SET
return true -- keep in SET
else
return false -- keep in SET
return false -- remove from SET
end
end
@@ -269,6 +291,7 @@ function TIRESIAS:_InitGroups()
-- Set all groups invisible/motionless
local EngageRange = self.AAARange
local SwitchAAA = self.SwitchAAA
local SwitchSAM = self.SwitchSAM
--- AAA
self.AAASet:ForEachGroupAlive(
function(grp)
@@ -288,7 +311,7 @@ function TIRESIAS:_InitGroups()
}
end
if grp.Tiresias and (not grp.Tiresias.exception == true) then
if grp.Tiresias.invisible and grp.Tiresias.invisible == false then
if grp.Tiresias.invisible == false then
grp:SetCommandInvisible(true)
grp.Tiresias.invisible = true
if SwitchAAA then
@@ -315,10 +338,11 @@ function TIRESIAS:_InitGroups()
}
end
if grp.Tiresias and (not grp.Tiresias.exception == true) then
if grp.Tiresias and grp.Tiresias.invisible and grp.Tiresias.invisible == false then
if grp.Tiresias and grp.Tiresias.invisible == false then
grp:SetCommandInvisible(true)
grp:SetAIOff()
grp.Tiresias.invisible = true
grp.Tiresias.AIOff = true
end
end
--BASE:I(string.format("Init/Switch off Vehicle %s (Exception %s)",grp:GetName(),tostring(grp.Tiresias.exception)))
@@ -332,16 +356,18 @@ function TIRESIAS:_InitGroups()
grp.Tiresias = { -- #TIRESIAS.Data
type = "SAM",
invisible = true,
exception = false,
exception = not SwitchSAM,
AIOff = SwitchSAM,
}
end
if grp.Tiresias and (not grp.Tiresias.exception == true) then
if grp.Tiresias and grp.Tiresias.invisible and grp.Tiresias.invisible == false then
if grp.Tiresias and grp.Tiresias.invisible == false then
grp:SetCommandInvisible(true)
grp.Tiresias.invisible = true
grp:SetAIOnOff(SwitchSAM)
end
end
--BASE:I(string.format("Init/Switch off SAM %s (Exception %s)",grp:GetName(),tostring(grp.Tiresias.exception)))
BASE:I(string.format("Init/Switch off SAM %s (Exception %s)",grp:GetName(),tostring(grp.Tiresias.exception)))
end
)
return self
@@ -381,18 +407,23 @@ end
function TIRESIAS:_SwitchOnGroups(group,radius)
self:T(self.lid.."_SwitchOnGroups "..group:GetName().." Radius "..radius.." NM")
local zone = ZONE_GROUP:New("Zone-"..group:GetName(),group,UTILS.NMToMeters(radius))
local samzone = ZONE_GROUP:New("ZoneSAM-"..group:GetName(),group,UTILS.NMToMeters(self.SAMSwitchRange))
local ground = SET_GROUP:New():FilterCategoryGround():FilterZones({zone}):FilterOnce()
local sam = SET_GROUP:New():FilterFunction(self._FilterSAM):FilterZones({samzone}):FilterOnce()
local count = ground:CountAlive()
if self.debug then
local text = string.format("There are %d groups around this plane or helo!",count)
self:I(text)
end
local SwitchAAA = self.SwitchAAA
local SwitchSAM = self.SwitchSAM
local MantisHandling = self.MantisHandlingSAM
if ground:CountAlive() > 0 then
ground:ForEachGroupAlive(
function(grp)
local name = grp:GetName()
if grp.Tiresias and grp.Tiresias.type and (not grp.Tiresias.exception == true ) then
if grp:GetCoalition() ~= group:GetCoalition()
and grp.Tiresias and grp.Tiresias.type and (not grp.Tiresias.exception == true ) then
if grp.Tiresias.invisible == true then
grp:SetCommandInvisible(false)
grp.Tiresias.invisible = false
@@ -401,18 +432,39 @@ function TIRESIAS:_SwitchOnGroups(group,radius)
grp:SetAIOn()
grp.Tiresias.AIOff = false
end
if SwitchAAA and grp.Tiresias.type == "AAA" and grp.Tiresias.AIOff and grp.Tiresias.AIOff == true then
if (SwitchAAA) and (grp.Tiresias.type == "AAA") and grp.Tiresias.AIOff and grp.Tiresias.AIOff == true then
grp:SetAIOn()
grp:EnableEmission(true)
grp.Tiresias.AIOff = false
end
--BASE:I(string.format("TIRESIAS - Switch on %s %s (Exception %s)",tostring(grp.Tiresias.type),grp:GetName(),tostring(grp.Tiresias.exception)))
end
BASE:I(string.format("TIRESIAS - Switch on %s %s (Exception %s)",tostring(grp.Tiresias.type),grp:GetName(),tostring(grp.Tiresias.exception)))
else
BASE:T("TIRESIAS - This group "..tostring(name).. " has not been initialized or is an exception!")
end
end
)
end
if sam:CountAlive() > 0 and self.SwitchSAM == true then
sam:ForEachGroupAlive(
function(grp)
local name = grp:GetName()
BASE:I(string.format("%s - %s",name,tostring(SwitchSAM)))
UTILS.PrintTableToLog(grp.Tiresias)
if SwitchSAM and grp.Tiresias.type == "SAM" and grp.Tiresias.AIOff and grp.Tiresias.AIOff == true then
BASE:I("First check passed")
if grp.Tiresias.exception ~= true then
BASE:I("Second check passed")
grp:SetAIOn()
if not MantisHandling then
grp:EnableEmission(true)
end
grp.Tiresias.AIOff = false
BASE:I(string.format("TIRESIAS - Switch on %s %s (Exception %s)",tostring(grp.Tiresias.type),grp:GetName(),tostring(grp.Tiresias.exception)))
end
end
end
)
end
return self
end
@@ -502,14 +554,22 @@ function TIRESIAS:onafterStart(From, Event, To)
end
end
local SwitchSAM = self.SwitchSAM
local MantisManages = self.MantisHandlingSAM
function SAMSet:OnAfterAdded(From,Event,To,ObjectName,Object)
if Object and Object:IsAlive() then
BASE:I("TIRESIAS: SAM Object Added: "..Object:GetName())
Object:SetCommandInvisible(true)
if SwitchSAM then
Object:SetAIOff()
Object:EnableEmission(not MantisManages)
end
Object.Tiresias = { -- #TIRESIAS.Data
type = "SAM",
invisible = true,
exception = false,
exception = not SwitchSAM,
AIOff = SwitchSAM,
}
end
end
@@ -547,7 +607,7 @@ end
-- @return #TIRESIAS self
function TIRESIAS:onafterStatus(From, Event, To)
self:T({From, Event, To})
if self.debug then
if self.debug then
local count = self.VehicleSet:CountAlive()
local AAAcount = self.AAASet:CountAlive()
local SAMcount = self.SAMSet:CountAlive()

View File

@@ -6047,7 +6047,7 @@ function WAREHOUSE:_SpawnAssetAircraft(alias, asset, request, parking, uncontrol
else
if #parking<#template.units and not airstart then
if parking and #parking<#template.units and not airstart then
local text=string.format("ERROR: Not enough parking! Free parking = %d < %d aircraft to be spawned.", #parking, #template.units)
self:_DebugMessage(text)
return nil
@@ -6089,7 +6089,7 @@ function WAREHOUSE:_SpawnAssetAircraft(alias, asset, request, parking, uncontrol
terminal=parking[i].TerminalID
end
if self.Debug then
if self.Debug and terminal then
local text=string.format("Spawnplace unit %s terminal %d.", unit.name, terminal)
coord:MarkToAll(text)
env.info(text)
@@ -8122,9 +8122,11 @@ function WAREHOUSE:_FindParkingForAssets(airbase, assets)
-- Debug output for occupied spots.
if self.Debug then
local coord=problem.coord --Core.Point#COORDINATE
local text=string.format("Obstacle %s [type=%s] blocking spot=%d! Size=%.1f m and distance=%.1f m.", problem.name, problem.type, _termid, problem.size, problem.dist)
self:I(self.lid..text)
coord:MarkToAll(string.format(text))
if coord then
local text=string.format("Obstacle %s [type=%s] blocking spot=%d! Size=%.1f m and distance=%.1f m.", problem.name, problem.type, _termid, problem.size, problem.dist)
self:I(self.lid..text)
coord:MarkToAll(text)
end
else
self:T(self.lid..string.format("Parking spot %d is occupied or not big enough!", _termid))
end
@@ -8433,12 +8435,14 @@ function WAREHOUSE:_GetAttribute(group)
local attribute=WAREHOUSE.Attribute.OTHER_UNKNOWN --#WAREHOUSE.Attribute
if group then
local groupCat=group:GetCategory()
-----------
--- Air ---
-----------
-- Planes
local transportplane=group:HasAttribute("Transports") and group:HasAttribute("Planes")
local transportplane=group:HasAttribute("Transports") and group:HasAttribute("Planes") and groupCat==Group.Category.AIRPLANE
local awacs=group:HasAttribute("AWACS")
local fighter=group:HasAttribute("Fighters") or group:HasAttribute("Interceptors") or group:HasAttribute("Multirole fighters") or (group:HasAttribute("Bombers") and not group:HasAttribute("Strategic bombers"))
local bomber=group:HasAttribute("Strategic bombers")
@@ -8593,7 +8597,6 @@ end
-- @param #WAREHOUSE.Queueitem qitem Item of queue to be removed.
-- @param #table queue The queue from which the item should be deleted.
function WAREHOUSE:_DeleteQueueItem(qitem, queue)
self:F({qitem=qitem, queue=queue})
for i=1,#queue do
local _item=queue[i] --#WAREHOUSE.Queueitem

View File

@@ -7,6 +7,8 @@
--
-- # Developer Note
--
-- ![Banner Image](..\Images\deprecated.png)
--
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
-- Therefore, this class is considered to be deprecated
--

View File

@@ -10,7 +10,7 @@ _SCHEDULEDISPATCHER = SCHEDULEDISPATCHER:New() -- Core.ScheduleDispatcher#SCHEDU
_DATABASE = DATABASE:New() -- Core.Database#DATABASE
--- Settings
_SETTINGS = SETTINGS:Set()
_SETTINGS = SETTINGS:Set() -- Core.Settings#SETTINGS
_SETTINGS:SetPlayerMenuOn()
--- Register cargos.

View File

@@ -1,8 +1,6 @@
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Utilities/Enums.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Utilities/Utils.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Utilities/Profiler.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Utilities/Templates.lua' )
--__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Utilities/STTS.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Utilities/FiFo.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Utilities/Socket.lua' )
@@ -51,12 +49,6 @@ __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Wrapper/Net.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Wrapper/Storage.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Wrapper/DynamicCargo.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Cargo/Cargo.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Cargo/CargoUnit.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Cargo/CargoSlingload.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Cargo/CargoCrate.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Cargo/CargoGroup.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Functional/Scoring.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Functional/CleanUp.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Functional/Movement.lua' )
@@ -119,43 +111,6 @@ __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/Squadron.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/Target.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/EasyGCICAP.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_Balancer.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_Air.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_Air_Patrol.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_Air_Engage.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_A2A_Patrol.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_A2A_Cap.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_A2A_Gci.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_A2A_Dispatcher.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_A2G_BAI.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_A2G_CAS.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_A2G_SEAD.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_A2G_Dispatcher.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_Patrol.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_CAP.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_CAS.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_BAI.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_Formation.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_Escort.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_Escort_Request.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_Escort_Dispatcher.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_Escort_Dispatcher_Request.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_Cargo.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_Cargo_APC.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_Cargo_Helicopter.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_Cargo_Airplane.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_Cargo_Ship.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_Cargo_Dispatcher.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_Cargo_Dispatcher_APC.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_Cargo_Dispatcher_Ship.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Actions/Act_Assign.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Actions/Act_Route.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Actions/Act_Account.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Actions/Act_Assist.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Shapes/ShapeBase.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Shapes/Circle.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Shapes/Cube.lua' )
@@ -171,21 +126,4 @@ __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Sound/RadioQueue.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Sound/RadioSpeech.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Sound/SRS.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Tasking/CommandCenter.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Tasking/Mission.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Tasking/Task.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Tasking/TaskInfo.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Tasking/Task_Manager.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Tasking/DetectionManager.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Tasking/Task_A2G_Dispatcher.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Tasking/Task_A2G.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Tasking/Task_A2A_Dispatcher.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Tasking/Task_A2A.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Tasking/Task_CARGO.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Tasking/Task_Cargo_Transport.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Tasking/Task_Cargo_CSAR.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Tasking/Task_Cargo_Dispatcher.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Tasking/Task_Capture_Zone.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Tasking/Task_Capture_Dispatcher.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Globals.lua' )

View File

@@ -1,7 +1,6 @@
__Moose.Include( 'Utilities\\Enums.lua' )
__Moose.Include( 'Utilities\\Utils.lua' )
__Moose.Include( 'Utilities\\Profiler.lua' )
--__Moose.Include( 'Utilities\\STTS.lua' )
__Moose.Include( 'Utilities\\FiFo.lua' )
__Moose.Include( 'Utilities\\Socket.lua' )
@@ -49,12 +48,6 @@ __Moose.Include( 'Wrapper\\Weapon.lua' )
__Moose.Include( 'Wrapper\\Storage.lua' )
__Moose.Include( 'Wrapper\\DynamicCargo.lua' )
__Moose.Include( 'Cargo\\Cargo.lua' )
__Moose.Include( 'Cargo\\CargoUnit.lua' )
__Moose.Include( 'Cargo\\CargoSlingload.lua' )
__Moose.Include( 'Cargo\\CargoCrate.lua' )
__Moose.Include( 'Cargo\\CargoGroup.lua' )
__Moose.Include( 'Functional\\Scoring.lua' )
__Moose.Include( 'Functional\\CleanUp.lua' )
__Moose.Include( 'Functional\\Movement.lua' )
@@ -115,44 +108,6 @@ __Moose.Include( 'Ops\\Operation.lua' )
__Moose.Include( 'Ops\\FlightControl.lua' )
__Moose.Include( 'Ops\\PlayerRecce.lua' )
__Moose.Include( 'Ops\\EasyGCICAP.lua' )
__Moose.Include( 'Ops\\EasyA2G.lua' )
__Moose.Include( 'AI\\AI_Balancer.lua' )
__Moose.Include( 'AI\\AI_Air.lua' )
__Moose.Include( 'AI\\AI_Air_Patrol.lua' )
__Moose.Include( 'AI\\AI_Air_Engage.lua' )
__Moose.Include( 'AI\\AI_A2A_Patrol.lua' )
__Moose.Include( 'AI\\AI_A2A_Cap.lua' )
__Moose.Include( 'AI\\AI_A2A_Gci.lua' )
__Moose.Include( 'AI\\AI_A2A_Dispatcher.lua' )
__Moose.Include( 'AI\\AI_A2G_BAI.lua' )
__Moose.Include( 'AI\\AI_A2G_CAS.lua' )
__Moose.Include( 'AI\\AI_A2G_SEAD.lua' )
__Moose.Include( 'AI\\AI_A2G_Dispatcher.lua' )
__Moose.Include( 'AI\\AI_Patrol.lua' )
__Moose.Include( 'AI\\AI_Cap.lua' )
__Moose.Include( 'AI\\AI_Cas.lua' )
__Moose.Include( 'AI\\AI_Bai.lua' )
__Moose.Include( 'AI\\AI_Formation.lua' )
__Moose.Include( 'AI\\AI_Escort.lua' )
__Moose.Include( 'AI\\AI_Escort_Request.lua' )
__Moose.Include( 'AI\\AI_Escort_Dispatcher.lua' )
__Moose.Include( 'AI\\AI_Escort_Dispatcher_Request.lua' )
__Moose.Include( 'AI\\AI_Cargo.lua' )
__Moose.Include( 'AI\\AI_Cargo_APC.lua' )
__Moose.Include( 'AI\\AI_Cargo_Helicopter.lua' )
__Moose.Include( 'AI\\AI_Cargo_Airplane.lua' )
__Moose.Include( 'AI\\AI_Cargo_Ship.lua' )
__Moose.Include( 'AI\\AI_Cargo_Dispatcher.lua' )
__Moose.Include( 'AI\\AI_Cargo_Dispatcher_APC.lua' )
__Moose.Include( 'AI\\AI_Cargo_Dispatcher_Helicopter.lua' )
__Moose.Include( 'AI\\AI_Cargo_Dispatcher_Airplane.lua' )
__Moose.Include( 'AI\\AI_Cargo_Dispatcher_Ship.lua' )
__Moose.Include( 'Actions\\Act_Assign.lua' )
__Moose.Include( 'Actions\\Act_Route.lua' )
__Moose.Include( 'Actions\\Act_Account.lua' )
__Moose.Include( 'Actions\\Act_Assist.lua' )
__Moose.Include( 'Sound\\UserSound.lua' )
__Moose.Include( 'Sound\\SoundOutput.lua' )
@@ -161,21 +116,4 @@ __Moose.Include( 'Sound\\RadioQueue.lua' )
__Moose.Include( 'Sound\\RadioSpeech.lua' )
__Moose.Include( 'Sound\\SRS.lua' )
__Moose.Include( 'Tasking\\CommandCenter.lua' )
__Moose.Include( 'Tasking\\Mission.lua' )
__Moose.Include( 'Tasking\\Task.lua' )
__Moose.Include( 'Tasking\\TaskInfo.lua' )
__Moose.Include( 'Tasking\\Task_Manager.lua' )
__Moose.Include( 'Tasking\\DetectionManager.lua' )
__Moose.Include( 'Tasking\\Task_A2G_Dispatcher.lua' )
__Moose.Include( 'Tasking\\Task_A2G.lua' )
__Moose.Include( 'Tasking\\Task_A2A_Dispatcher.lua' )
__Moose.Include( 'Tasking\\Task_A2A.lua' )
__Moose.Include( 'Tasking\\Task_Cargo.lua' )
__Moose.Include( 'Tasking\\Task_Cargo_Transport.lua' )
__Moose.Include( 'Tasking\\Task_Cargo_CSAR.lua' )
__Moose.Include( 'Tasking\\Task_Cargo_Dispatcher.lua' )
__Moose.Include( 'Tasking\\Task_Capture_Zone.lua' )
__Moose.Include( 'Tasking\\Task_Capture_Dispatcher.lua' )
__Moose.Include( 'Globals.lua' )

View File

@@ -19,7 +19,7 @@
-- * Option to present information in imperial or metric units
-- * Runway length and airfield elevation (optional)
-- * Frequencies/channels of nav aids (ILS, VOR, NDB, TACAN, PRMG, RSBN) (optional)
-- * SRS Simple-Text-To-Speech (STTS) integration (no sound files necessary)
-- * SRS Simple-Text-To-Speech (MSRS) integration (no sound files necessary)
--
-- ===
--
@@ -291,7 +291,7 @@
-- ## Nevada: Nellis AFB
--
-- -- ATIS Nellis AFB on 270.10 MHz AM.
-- atisNellis=ATIS:New(AIRBASE.Nevada.Nellis_AFB, 270.1)
-- atisNellis=ATIS:New(AIRBASE.Nevada.Nellis, 270.1)
-- atisNellis:SetRadioRelayUnitName("Radio Relay Nellis")
-- atisNellis:SetActiveRunway("21L")
-- atisNellis:SetTowerFrequencies({327.000, 132.550})
@@ -302,7 +302,7 @@
-- ## Persian Gulf: Abu Dhabi International Airport
--
-- -- ATIS Abu Dhabi International on 125.1 MHz AM.
-- atisAbuDhabi=ATIS:New(AIRBASE.PersianGulf.Abu_Dhabi_International_Airport, 125.1)
-- atisAbuDhabi=ATIS:New(AIRBASE.PersianGulf.Abu_Dhabi_Intl, 125.1)
-- atisAbuDhabi:SetRadioRelayUnitName("Radio Relay Abu Dhabi International Airport")
-- atisAbuDhabi:SetMetricUnits()
-- atisAbuDhabi:SetActiveRunway("L")
@@ -498,6 +498,10 @@ ATIS.Alphabet = {
-- @field #number Syria +5° (East).
-- @field #number MarianaIslands +2° (East).
-- @field #number SinaiMap +5° (East).
-- @field #number Kola +15° (East).
-- @field #number Afghanistan +3° (East).
-- @field #number Iraq +4.4° (East).
-- @field #number GermanyCW +0.1° (East).
ATIS.RunwayM2T = {
Caucasus = 0,
Nevada = 12,
@@ -508,6 +512,10 @@ ATIS.RunwayM2T = {
MarianaIslands = 2,
Falklands = 12,
SinaiMap = 5,
Kola = 15,
Afghanistan = 3,
Iraq=4.4,
GermanyCW=0.1,
}
--- Whether ICAO phraseology is used for ATIS broadcasts.
@@ -521,6 +529,10 @@ ATIS.RunwayM2T = {
-- @field #boolean MarianaIslands true.
-- @field #boolean Falklands true.
-- @field #boolean SinaiMap true.
-- @field #boolean Kola true.
-- @field #boolean Afghanistan true.
-- @field #boolean Iraq true.
-- @field #boolean GermanyCW true.
ATIS.ICAOPhraseology = {
Caucasus = true,
Nevada = false,
@@ -531,6 +543,10 @@ ATIS.ICAOPhraseology = {
MarianaIslands = true,
Falklands = true,
SinaiMap = true,
Kola = true,
Afghanistan = true,
Iraq = true,
GermanyCW = true,
}
--- Nav point data.
@@ -619,83 +635,83 @@ ATIS.ICAOPhraseology = {
-- @field #ATIS.Soundfile TACANChannel
-- @field #ATIS.Soundfile VORFrequency
ATIS.Sound = {
ActiveRunway = { filename = "ActiveRunway.ogg", duration = 0.99 },
ActiveRunwayDeparture = { filename = "ActiveRunwayDeparture.ogg", duration = 0.99 },
ActiveRunwayArrival = { filename = "ActiveRunwayArrival.ogg", duration = 0.99 },
AdviceOnInitial = { filename = "AdviceOnInitial.ogg", duration = 3.00 },
Airport = { filename = "Airport.ogg", duration = 0.66 },
Altimeter = { filename = "Altimeter.ogg", duration = 0.68 },
At = { filename = "At.ogg", duration = 0.41 },
CloudBase = { filename = "CloudBase.ogg", duration = 0.82 },
CloudCeiling = { filename = "CloudCeiling.ogg", duration = 0.61 },
CloudsBroken = { filename = "CloudsBroken.ogg", duration = 1.07 },
CloudsFew = { filename = "CloudsFew.ogg", duration = 0.99 },
CloudsNo = { filename = "CloudsNo.ogg", duration = 1.01 },
CloudsNotAvailable = { filename = "CloudsNotAvailable.ogg", duration = 2.35 },
CloudsOvercast = { filename = "CloudsOvercast.ogg", duration = 0.83 },
CloudsScattered = { filename = "CloudsScattered.ogg", duration = 1.18 },
Decimal = { filename = "Decimal.ogg", duration = 0.54 },
DegreesCelsius = { filename = "DegreesCelsius.ogg", duration = 1.27 },
DegreesFahrenheit = { filename = "DegreesFahrenheit.ogg", duration = 1.23 },
DewPoint = { filename = "DewPoint.ogg", duration = 0.65 },
Dust = { filename = "Dust.ogg", duration = 0.54 },
Elevation = { filename = "Elevation.ogg", duration = 0.78 },
EndOfInformation = { filename = "EndOfInformation.ogg", duration = 1.15 },
Feet = { filename = "Feet.ogg", duration = 0.45 },
Fog = { filename = "Fog.ogg", duration = 0.47 },
Gusting = { filename = "Gusting.ogg", duration = 0.55 },
HectoPascal = { filename = "HectoPascal.ogg", duration = 1.15 },
Hundred = { filename = "Hundred.ogg", duration = 0.47 },
InchesOfMercury = { filename = "InchesOfMercury.ogg", duration = 1.16 },
Information = { filename = "Information.ogg", duration = 0.85 },
Kilometers = { filename = "Kilometers.ogg", duration = 0.78 },
Knots = { filename = "Knots.ogg", duration = 0.59 },
Left = { filename = "Left.ogg", duration = 0.54 },
MegaHertz = { filename = "MegaHertz.ogg", duration = 0.87 },
Meters = { filename = "Meters.ogg", duration = 0.59 },
MetersPerSecond = { filename = "MetersPerSecond.ogg", duration = 1.14 },
Miles = { filename = "Miles.ogg", duration = 0.60 },
MillimetersOfMercury = { filename = "MillimetersOfMercury.ogg", duration = 1.53 },
Minus = { filename = "Minus.ogg", duration = 0.64 },
N0 = { filename = "N-0.ogg", duration = 0.55 },
N1 = { filename = "N-1.ogg", duration = 0.41 },
N2 = { filename = "N-2.ogg", duration = 0.37 },
N3 = { filename = "N-3.ogg", duration = 0.41 },
N4 = { filename = "N-4.ogg", duration = 0.37 },
N5 = { filename = "N-5.ogg", duration = 0.43 },
N6 = { filename = "N-6.ogg", duration = 0.55 },
N7 = { filename = "N-7.ogg", duration = 0.43 },
N8 = { filename = "N-8.ogg", duration = 0.38 },
N9 = { filename = "N-9.ogg", duration = 0.55 },
NauticalMiles = { filename = "NauticalMiles.ogg", duration = 1.04 },
None = { filename = "None.ogg", duration = 0.43 },
QFE = { filename = "QFE.ogg", duration = 0.63 },
QNH = { filename = "QNH.ogg", duration = 0.71 },
Rain = { filename = "Rain.ogg", duration = 0.41 },
Right = { filename = "Right.ogg", duration = 0.44 },
Snow = { filename = "Snow.ogg", duration = 0.48 },
SnowStorm = { filename = "SnowStorm.ogg", duration = 0.82 },
StatuteMiles = { filename = "StatuteMiles.ogg", duration = 1.15 },
SunriseAt = { filename = "SunriseAt.ogg", duration = 0.92 },
SunsetAt = { filename = "SunsetAt.ogg", duration = 0.95 },
Temperature = { filename = "Temperature.ogg", duration = 0.64 },
Thousand = { filename = "Thousand.ogg", duration = 0.55 },
ThunderStorm = { filename = "ThunderStorm.ogg", duration = 0.81 },
TimeLocal = { filename = "TimeLocal.ogg", duration = 0.90 },
TimeZulu = { filename = "TimeZulu.ogg", duration = 0.86 },
TowerFrequency = { filename = "TowerFrequency.ogg", duration = 1.19 },
Visibilty = { filename = "Visibility.ogg", duration = 0.79 },
WeatherPhenomena = { filename = "WeatherPhenomena.ogg", duration = 1.07 },
WindFrom = { filename = "WindFrom.ogg", duration = 0.60 },
ActiveRunway = { filename = "ActiveRunway.ogg", duration = 0.85 },
ActiveRunwayDeparture = { filename = "ActiveRunwayDeparture.ogg", duration = 1.50 },
ActiveRunwayArrival = { filename = "ActiveRunwayArrival.ogg", duration = 1.38 },
AdviceOnInitial = { filename = "AdviceOnInitial.ogg", duration = 2.98 },
Airport = { filename = "Airport.ogg", duration = 0.55 },
Altimeter = { filename = "Altimeter.ogg", duration = 0.91 },
At = { filename = "At.ogg", duration = 0.32 },
CloudBase = { filename = "CloudBase.ogg", duration = 0.69 },
CloudCeiling = { filename = "CloudCeiling.ogg", duration = 0.53 },
CloudsBroken = { filename = "CloudsBroken.ogg", duration = 0.81 },
CloudsFew = { filename = "CloudsFew.ogg", duration = 0.74 },
CloudsNo = { filename = "CloudsNo.ogg", duration = 0.69},
CloudsNotAvailable = { filename = "CloudsNotAvailable.ogg", duration = 2.64 },
CloudsOvercast = { filename = "CloudsOvercast.ogg", duration = 0.82 },
CloudsScattered = { filename = "CloudsScattered.ogg", duration = 0.89 },
Decimal = { filename = "Decimal.ogg", duration = 0.71 },
DegreesCelsius = { filename = "DegreesCelsius.ogg", duration = 1.08 },
DegreesFahrenheit = { filename = "DegreesFahrenheit.ogg", duration = 1.07 },
DewPoint = { filename = "DewPoint.ogg", duration = 0.59 },
Dust = { filename = "Dust.ogg", duration = 0.37 },
Elevation = { filename = "Elevation.ogg", duration = 0.92 },
EndOfInformation = { filename = "EndOfInformation.ogg", duration = 1.24 },
Feet = { filename = "Feet.ogg", duration = 0.34 },
Fog = { filename = "Fog.ogg", duration = 0.41 },
Gusting = { filename = "Gusting.ogg", duration = 0.58 },
HectoPascal = { filename = "HectoPascal.ogg", duration = 0.92 },
Hundred = { filename = "Hundred.ogg", duration = 0.53 },
ILSFrequency = { filename = "ILSFrequency.ogg", duration = 1.30 },
InnerNDBFrequency = { filename = "InnerNDBFrequency.ogg", duration = 1.56 },
OuterNDBFrequency = { filename = "OuterNDBFrequency.ogg", duration = 1.59 },
RunwayLength = { filename = "RunwayLength.ogg", duration = 0.91 },
VORFrequency = { filename = "VORFrequency.ogg", duration = 1.38 },
TACANChannel = { filename = "TACANChannel.ogg", duration = 0.88 },
PRMGChannel = { filename = "PRMGChannel.ogg", duration = 1.18 },
RSBNChannel = { filename = "RSBNChannel.ogg", duration = 1.14 },
Zulu = { filename = "Zulu.ogg", duration = 0.62 },
InchesOfMercury = { filename = "InchesOfMercury.ogg", duration = 1.26 },
Information = { filename = "Information.ogg", duration = 0.99 },
InnerNDBFrequency = { filename = "InnerNDBFrequency.ogg", duration = 1.69 },
Kilometers = { filename = "Kilometers.ogg", duration = 0.93 },
Knots = { filename = "Knots.ogg", duration = 0.46 },
Left = { filename = "Left.ogg", duration = 0.41 },
MegaHertz = { filename = "MegaHertz.ogg", duration = 0.83 },
Meters = { filename = "Meters.ogg", duration = 0.55 },
MetersPerSecond = { filename = "MetersPerSecond.ogg", duration = 1.03 },
Miles = { filename = "Miles.ogg", duration = 0.44 },
MillimetersOfMercury = { filename = "MillimetersOfMercury.ogg", duration = 1.59 },
Minus = { filename = "Minus.ogg", duration = 0.55 },
N0 = { filename = "N-0.ogg", duration = 0.52 },
N1 = { filename = "N-1.ogg", duration = 0.35 },
N2 = { filename = "N-2.ogg", duration = 0.41 },
N3 = { filename = "N-3.ogg", duration = 0.34 },
N4 = { filename = "N-4.ogg", duration = 0.37 },
N5 = { filename = "N-5.ogg", duration = 0.40 },
N6 = { filename = "N-6.ogg", duration = 0.46 },
N7 = { filename = "N-7.ogg", duration = 0.52 },
N8 = { filename = "N-8.ogg", duration = 0.36 },
N9 = { filename = "N-9.ogg", duration = 0.51 },
NauticalMiles = { filename = "NauticalMiles.ogg", duration = 0.93 },
None = { filename = "None.ogg", duration = 0.33 },
OuterNDBFrequency = { filename = "OuterNDBFrequency.ogg", duration = 1.70 },
PRMGChannel = { filename = "PRMGChannel.ogg", duration = 1.27 },
QFE = { filename = "QFE.ogg", duration = 0.90 },
QNH = { filename = "QNH.ogg", duration = 0.94 },
Rain = { filename = "Rain.ogg", duration = 0.35 },
Right = { filename = "Right.ogg", duration = 0.31 },
RSBNChannel = { filename = "RSBNChannel.ogg", duration = 1.26 },
RunwayLength = { filename = "RunwayLength.ogg", duration = 0.81 },
Snow = { filename = "Snow.ogg", duration = 0.40 },
SnowStorm = { filename = "SnowStorm.ogg", duration = 0.73 },
StatuteMiles = { filename = "StatuteMiles.ogg", duration = 0.90 },
SunriseAt = { filename = "SunriseAt.ogg", duration = 0.82 },
SunsetAt = { filename = "SunsetAt.ogg", duration = 0.87 },
TACANChannel = { filename = "TACANChannel.ogg", duration = 0.81 },
Temperature = { filename = "Temperature.ogg", duration = 0.70 },
Thousand = { filename = "Thousand.ogg", duration = 0.58 },
ThunderStorm = { filename = "ThunderStorm.ogg", duration = 0.79 },
TimeLocal = { filename = "TimeLocal.ogg", duration = 0.83 },
TimeZulu = { filename = "TimeZulu.ogg", duration = 0.83 },
TowerFrequency = { filename = "TowerFrequency.ogg", duration = 1.05 },
Visibilty = { filename = "Visibility.ogg", duration = 1.16 },
VORFrequency = { filename = "VORFrequency.ogg", duration = 1.28 },
WeatherPhenomena = { filename = "WeatherPhenomena.ogg", duration = 1.09 },
WindFrom = { filename = "WindFrom.ogg", duration = 0.63 },
Zulu = { filename = "Zulu.ogg", duration = 0.51 },
}
---
@@ -954,7 +970,7 @@ _ATIS = {}
--- ATIS class version.
-- @field #string version
ATIS.version = "1.0.0"
ATIS.version = "1.0.1"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
@@ -2037,12 +2053,14 @@ function ATIS:onafterBroadcast( From, Event, To )
local sunrise = coord:GetSunrise()
--self:I(sunrise)
local SUNRISE = "no time"
local NorthPolar = true
if tostring(sunrise) ~= "N/S" and tostring(sunrise) ~= "N/R" then
sunrise = UTILS.Split( sunrise, ":" )
SUNRISE = string.format( "%s%s", sunrise[1], sunrise[2] )
if self.useSRS then
SUNRISE = string.format( "%s %s %s", sunrise[1], sunrise[2], hours )
end
NorthPolar = false
end
local sunset = coord:GetSunset()
@@ -2054,6 +2072,7 @@ function ATIS:onafterBroadcast( From, Event, To )
if self.useSRS then
SUNSET = string.format( "%s %s %s", sunset[1], sunset[2], hours )
end
NorthPolar = false
end
---------------------------------
@@ -2080,34 +2099,32 @@ function ATIS:onafterBroadcast( From, Event, To )
---------------
-- Get mission weather info. Most of this is static.
local clouds, visibility, turbulence, fog, dust, static = self:GetMissionWeather()
-- Check that fog is actually "thick" enough to reach the airport. If an airport is in the mountains, fog might not affect it as it is measured from sea level.
if fog and fog.thickness < height + 25 then
fog = nil
end
-- Dust only up to 1500 ft = 457 m ASL.
if dust and height + 25 > UTILS.FeetToMeters( 1500 ) then
dust = nil
end
local clouds, visibility, turbulence, dustdens, static = self:GetMissionWeather()
local dust=false
local fog=false
------------------
--- Visibility ---
------------------
-- Get min visibility.
local visibilitymin = visibility
if fog then
if fog.visibility < visibilitymin then
visibilitymin = fog.visibility
if dustdens then
-- Dust only up to 1500 ft = 457 m ASL.
if UTILS.FeetToMeters( 1500 )> height+25 then
dust=true
visibility=math.min(visibility, dustdens)
end
end
if dust then
if dust < visibilitymin then
visibilitymin = dust
else -- As of DCS 2.9.10.3948 (December 2024), fog and dust are mutually exclusive!
-- Get current fog visibility and thickness
local fvis=world.weather.getFogVisibilityDistance()
local fheight=world.weather.getFogThickness()
if fvis>0 and fheight>height+25 then
fog=true
visibility=math.min(visibility, fvis)
end
end
@@ -2115,7 +2132,7 @@ function ATIS:onafterBroadcast( From, Event, To )
if self.metric then
-- Visibility in km.
local reportedviz = UTILS.Round( visibilitymin / 1000 )
local reportedviz = UTILS.Round( visibility / 1000 )
-- max reported visibility 9999 m
if reportedviz > 10 then
reportedviz = 10
@@ -2123,7 +2140,7 @@ function ATIS:onafterBroadcast( From, Event, To )
VISIBILITY = string.format( "%d", reportedviz )
else
-- max reported visibility 10 NM
local reportedviz = UTILS.Round( UTILS.MetersToSM( visibilitymin ) )
local reportedviz = UTILS.Round( UTILS.MetersToSM( visibility ) )
if reportedviz > 10 then
reportedviz = 10
end
@@ -2395,7 +2412,7 @@ function ATIS:onafterBroadcast( From, Event, To )
local sunrise = self.gettext:GetEntry("SUNRISEAT",self.locale)
--subtitle = string.format( "Sunrise at %s local time", SUNRISE )
subtitle = string.format( sunrise, SUNRISE )
if not self.useSRS then
if not self.useSRS and NorthPolar == false then
self:Transmission( self.Sound.SunriseAt, 0.5, subtitle )
self.radioqueue:Number2Transmission( SUNRISE, nil, 0.2 )
self:Transmission( self.Sound.TimeLocal, 0.2 )
@@ -2406,7 +2423,7 @@ function ATIS:onafterBroadcast( From, Event, To )
local sunset = self.gettext:GetEntry("SUNSETAT",self.locale)
--subtitle = string.format( "Sunset at %s local time", SUNSET )
subtitle = string.format( sunset, SUNSET )
if not self.useSRS then
if not self.useSRS and NorthPolar == false then
self:Transmission( self.Sound.SunsetAt, 0.5, subtitle )
self.radioqueue:Number2Transmission( SUNSET, nil, 0.5 )
self:Transmission( self.Sound.TimeLocal, 0.2 )
@@ -3350,28 +3367,13 @@ function ATIS:GetMissionWeather()
dust = weather.dust_density
end
-- Fog
--[[
["enable_fog"] = false,
["fog"] =
{
["thickness"] = 0,
["visibility"] = 25,
}, -- end of ["fog"]
]]
local fog = nil
if weather.enable_fog == true then
fog = weather.fog
end
self:T( "FF weather:" )
self:T( { clouds = clouds } )
self:T( { visibility = visibility } )
self:T( { turbulence = turbulence } )
self:T( { fog = fog } )
self:T( { dust = dust } )
self:T( { static = static } )
return clouds, visibility, turbulence, fog, dust, static
return clouds, visibility, turbulence, dust, static
end
--- Get thousands of a number.

View File

@@ -187,7 +187,7 @@ AIRWING = {
--- AIRWING class version.
-- @field #string version
AIRWING.version="0.9.5"
AIRWING.version="0.9.6"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ToDo list
@@ -1365,16 +1365,20 @@ function AIRWING:CheckRescuhelo()
local N=self:CountMissionsInQueue({AUFTRAG.Type.RESCUEHELO})
local name=self.airbase:GetName()
local carrier=UNIT:FindByName(name)
for i=1,self.nflightsRescueHelo-N do
local mission=AUFTRAG:NewRESCUEHELO(carrier)
self:AddMission(mission)
if self.airbase then
local name=self.airbase:GetName()
local carrier=UNIT:FindByName(name)
for i=1,self.nflightsRescueHelo-N do
local mission=AUFTRAG:NewRESCUEHELO(carrier)
self:AddMission(mission)
end
end
return self

View File

@@ -1731,10 +1731,10 @@ AIRBOSS.Difficulty = {
-- @field #table trapsheet Groove data table recorded every 0.5 seconds.
-- @field #boolean trapon If true, save trap sheets.
-- @field #string debriefschedulerID Debrief scheduler ID.
--
--
-- @field Sound.SRS#MSRS SRS
-- @field Sound.SRS#MSRSQUEUE SRSQ
--
--
-- @extends #AIRBOSS.FlightGroup
--- Main group level radio menu: F10 Other/Airboss.
@@ -1912,6 +1912,9 @@ function AIRBOSS:New( carriername, alias )
-- Set max section members. Default 2.
self:SetMaxSectionSize()
-- Set max section distance. Default 100 meters.
self:SetMaxSectionDistance()
-- Set max flights per stack. Default is 2.
self:SetMaxFlightsPerStack()
@@ -2539,7 +2542,7 @@ function AIRBOSS:AddRecoveryWindow( starttime, stoptime, case, holdingoffset, tu
return self
end
if Tstop <= Tnow then
string.format( "WARNING: Recovery stop time %s already over. Tnow=%s! Recovery window rejected.", UTILS.SecondsToClock( Tstop ), UTILS.SecondsToClock( Tnow ) )
string.format( "WARNING: Recovery stop time %s already over. Tnow=%s! Recovery window rejected.", UTILS.SecondsToClock( Tstop ), UTILS.SecondsToClock( Tnow ) )
return self
end
@@ -3066,7 +3069,7 @@ end
-- @param #number Port Port of the SRS server, defaults to 5002.
-- @param #string Culture (Optional, Airboss Culture) Culture, defaults to "en-US".
-- @param #string Gender (Optional, Airboss Gender) Gender, e.g. "male" or "female". Defaults to "male".
-- @param #string Voice (Optional, Airboss Voice) Set to use a specific voice. Will **override gender and culture** settings.
-- @param #string Voice (Optional, Airboss Voice) Set to use a specific voice. Will **override gender and culture** settings.
-- @param #string GoogleCreds (Optional) Path to Google credentials, e.g. "C:\\Program Files\\DCS-SimpleRadio-Standalone\\yourgooglekey.json".
-- @param #number Volume (Optional) E.g. 0.75. Defaults to 1.0 (loudest).
-- @param #table AltBackend (Optional) See MSRS for details.
@@ -3098,10 +3101,10 @@ function AIRBOSS:EnableSRS(PathToSRS,Port,Culture,Gender,Voice,GoogleCreds,Volum
-- SRSQUEUE
self.SRSQ = MSRSQUEUE:New("AIRBOSS")
self.SRSQ:SetTransmitOnlyWithPlayers(true)
if not self.PilotRadio then
if not self.PilotRadio then
self:SetSRSPilotVoice()
end
return self
return self
end
--- Set LSO radio frequency and modulation. Default frequency is 264 MHz AM.
@@ -3344,6 +3347,22 @@ function AIRBOSS:SetMaxSectionSize( nmax )
return self
end
--- Set maximum distance up to which section members are allowed (default: 100 meters).
-- @param #AIRBOSS self
-- @param #number dmax Max distance in meters (default 100 m). Minimum is 10 m, maximum is 5000 m.
-- @return #AIRBOSS self
function AIRBOSS:SetMaxSectionDistance( dmax )
if dmax then
if dmax < 10 then
dmax = 10
elseif dmax > 5000 then
dmax = 5000
end
end
self.maxsectiondistance = dmax or 100
return self
end
--- Set max number of flights per stack. All members of a section count as one "flight".
-- @param #AIRBOSS self
-- @param #number nmax Number of max allowed flights per stack. Default is two. Minimum is one, maximum is 4.
@@ -3615,7 +3634,7 @@ function AIRBOSS:onafterStart( From, Event, To )
-- Handle events.
self:HandleEvent( EVENTS.Birth )
self:HandleEvent( EVENTS.Land )
self:HandleEvent( EVENTS.RunwayTouch )
self:HandleEvent( EVENTS.EngineShutdown )
self:HandleEvent( EVENTS.Takeoff )
self:HandleEvent( EVENTS.Crash )
@@ -4381,7 +4400,7 @@ function AIRBOSS:onafterStop( From, Event, To )
-- Unhandle events.
self:UnHandleEvent( EVENTS.Birth )
self:UnHandleEvent( EVENTS.Land )
self:UnHandleEvent( EVENTS.RunwayTouch )
self:UnHandleEvent( EVENTS.EngineShutdown )
self:UnHandleEvent( EVENTS.Takeoff )
self:UnHandleEvent( EVENTS.Crash )
@@ -8291,7 +8310,7 @@ end
--- Airboss event handler for event land.
-- @param #AIRBOSS self
-- @param Core.Event#EVENTDATA EventData
function AIRBOSS:OnEventLand( EventData )
function AIRBOSS:OnEventRunwayTouch( EventData )
self:F3( { eventland = EventData } )
-- Nil checks.
@@ -11598,7 +11617,7 @@ function AIRBOSS:GetHeadingIntoWind_old( vdeck, magnetic, coord )
local function adjustDegreesForWindSpeed(windSpeed)
local degreesAdjustment = 0
-- the windspeeds are in m/s
-- +0 degrees at 15m/s = 37kts
-- +0 degrees at 14m/s = 35kts
-- +0 degrees at 13m/s = 33kts
@@ -11613,7 +11632,7 @@ function AIRBOSS:GetHeadingIntoWind_old( vdeck, magnetic, coord )
-- +20 degrees at 4m/s = 26kts
-- +20 degrees at 3m/s = 26kts
-- +30 degrees at 2m/s = 26kts 1s
if windSpeed > 0 and windSpeed < 3 then
degreesAdjustment = 30
elseif windSpeed >= 3 and windSpeed < 5 then
@@ -11625,7 +11644,7 @@ function AIRBOSS:GetHeadingIntoWind_old( vdeck, magnetic, coord )
elseif windSpeed >= 13 then
degreesAdjustment = 0
end
return degreesAdjustment
end
@@ -11684,60 +11703,60 @@ function AIRBOSS:GetHeadingIntoWind_new( vdeck, magnetic, coord )
local h=self:GetHeading(magnetic)
return h, math.min(vdeck, Vmax)
end
-- Convert wind speed to knots.
vwind=UTILS.MpsToKnots(vwind)
-- Wind to in knots.
local windto=(windfrom+180)%360
-- Offset angle in rad. We also define the rotation to be clock-wise, which requires a minus sign.
local alpha=math.rad(-Offset)
-- Constant.
local C = math.sqrt(math.cos(alpha)^2 / math.sin(alpha)^2 + 1)
-- Upper limit of desired speed due to max boat speed.
local vdeckMax=vwind + math.cos(alpha) * Vmax
-- Lower limit of desired speed due to min boat speed.
local vdeckMin=vwind + math.cos(alpha) * Vmin
-- Speed of ship so it matches the desired speed.
local v=0
-- Angle wrt. to wind TO-direction
-- Angle wrt. to wind TO-direction
local theta=0
if vdeck>vdeckMax then
-- Boat cannot go fast enough
-- Set max speed.
v=Vmax
-- Calculate theta.
theta = math.asin(v/(vwind*C)) - math.asin(-1/C)
elseif vdeck<vdeckMin then
-- Boat cannot go slow enought
-- Set min speed.
v=Vmin
-- Calculatge theta.
theta = math.asin(v/(vwind*C)) - math.asin(-1/C)
elseif vdeck*math.sin(alpha)>vwind then
-- Too little wind
-- Set theta to 90°
theta=math.pi/2
-- Set speed.
v = math.sqrt(vdeck^2 - vwind^2)
else
-- Normal case
theta = math.asin(vdeck * math.sin(alpha) / vwind)
@@ -11746,9 +11765,9 @@ function AIRBOSS:GetHeadingIntoWind_new( vdeck, magnetic, coord )
-- Magnetic heading.
local magvar= magnetic and self.magvar or 0
-- Ship heading so cross wind is min for the given wind.
local intowind = (540 + (windto - magvar + math.deg(theta) )) % 360
local intowind = (540 + (windto - magvar + math.deg(theta) )) % 360
return intowind, v
end
@@ -12206,7 +12225,7 @@ function AIRBOSS:_LSOgrade( playerData )
-- Normal laning part at the beginning
local Gb = GXX .. " " .. GIM
-- Number of deviations that occurred at the the beginning of the landing (XX or IM). These are graded like in non-VTOL landings, i.e. on deviations is
-- Number of deviations that occurred at the the beginning of the landing (XX or IM). These are graded like in non-VTOL landings, i.e. on deviations is
local N=nXX+nIM
local nL=count(Gb, '_')/2
local nS=count(Gb, '%(')
@@ -12224,7 +12243,7 @@ function AIRBOSS:_LSOgrade( playerData )
if nL>0 or nLv>1 then
-- Larger deviations at XX or IM or at least one larger deviation IC or AR==> "No grade" 2.0 points.
-- In other words, we allow one larger deviation at IC+AR
-- In other words, we allow one larger deviation at IC+AR
grade="--"
points=2.0
elseif nN>0 or nNv>1 or nLv==1 then
@@ -13720,7 +13739,7 @@ function AIRBOSS:CarrierTurnIntoWind( time, vdeck, uturn )
local deltaH = self:_GetDeltaHeading( hdg, hiw )
-- Debug output
self:I( self.lid .. string.format( "Carrier steaming into the wind (%.1f kts). Heading=%03d-->%03d (Delta=%.1f), Speed=%.1f knots, Distance=%.1f NM, Time=%d sec",
self:I( self.lid .. string.format( "Carrier steaming into the wind (%.1f kts). Heading=%03d-->%03d (Delta=%.1f), Speed=%.1f knots, Distance=%.1f NM, Time=%d sec",
UTILS.MpsToKnots( vwind ), hdg, hiw, deltaH, speedknots, distNM, speedknots, time ) )
-- Current coordinate.
@@ -14932,12 +14951,12 @@ function AIRBOSS:RadioTransmission( radio, call, loud, delay, interval, click, p
if radio == nil or call == nil then
return
end
if not self.SRS then
-- Create a new radio transmission item.
local transmission = {} -- #AIRBOSS.Radioitem
transmission.radio = radio
transmission.call = call
transmission.Tplay = timer.getAbsTime() + (delay or 0)
@@ -14945,49 +14964,49 @@ function AIRBOSS:RadioTransmission( radio, call, loud, delay, interval, click, p
transmission.isplaying = false
transmission.Tstarted = nil
transmission.loud = loud and call.loud
-- Player onboard number if sender has one.
if self:_IsOnboard( call.modexsender ) then
self:_Number2Radio( radio, call.modexsender, delay, 0.3, pilotcall )
end
-- Play onboard number if receiver has one.
if self:_IsOnboard( call.modexreceiver ) then
self:_Number2Radio( radio, call.modexreceiver, delay, 0.3, pilotcall )
end
-- Add transmission to the right queue.
local caller = ""
if radio.alias == "LSO" then
table.insert( self.RQLSO, transmission )
caller = "LSOCall"
-- Schedule radio queue checks.
if not self.RQLid then
self:T( self.lid .. string.format( "Starting LSO radio queue." ) )
self.RQLid = self.radiotimer:Schedule( nil, AIRBOSS._CheckRadioQueue, { self, self.RQLSO, "LSO" }, 0.02, 0.05 )
end
elseif radio.alias == "MARSHAL" then
table.insert( self.RQMarshal, transmission )
caller = "MarshalCall"
if not self.RQMid then
self:T( self.lid .. string.format( "Starting Marhal radio queue." ) )
self.RQMid = self.radiotimer:Schedule( nil, AIRBOSS._CheckRadioQueue, { self, self.RQMarshal, "MARSHAL" }, 0.02, 0.05 )
end
end
-- Append radio click sound at the end of the transmission.
if click then
self:RadioTransmission( radio, self[caller].CLICK, false, delay )
end
else
-- SRS transmission
@@ -14998,7 +15017,7 @@ function AIRBOSS:RadioTransmission( radio, call, loud, delay, interval, click, p
local voice = nil
local gender = nil
local culture = nil
if radio.alias == "AIRBOSS" then
frequency = self.AirbossRadio.frequency
modulation = self.AirbossRadio.modulation
@@ -15006,13 +15025,13 @@ function AIRBOSS:RadioTransmission( radio, call, loud, delay, interval, click, p
gender = self.AirbossRadio.gender
culture = self.AirbossRadio.culture
end
if radio.alias == "MARSHAL" then
voice = self.MarshalRadio.voice
gender = self.MarshalRadio.gender
culture = self.MarshalRadio.culture
end
if radio.alias == "LSO" then
frequency = self.LSORadio.frequency
modulation = self.LSORadio.modulation
@@ -15020,7 +15039,7 @@ function AIRBOSS:RadioTransmission( radio, call, loud, delay, interval, click, p
gender = self.LSORadio.gender
culture = self.LSORadio.culture
end
if pilotcall then
voice = self.PilotRadio.voice
gender = self.PilotRadio.gender
@@ -15034,16 +15053,16 @@ function AIRBOSS:RadioTransmission( radio, call, loud, delay, interval, click, p
modulation = self.AirbossRadio.modulation
radio.alias = "AIRBOSS"
end
local volume = nil
if loud then
volume = 1.0
end
--local text = tostring(call.modexreceiver).."; "..radio.alias.."; "..call.subtitle
local text = call.subtitle
self:T(self.lid..text)
self:T(self.lid..text)
local srstext = self:_GetNiceSRSText(text)
self.SRSQ:NewTransmission(srstext, call.duration, self.SRS, nil, 0.1, nil, call.subtitle, call.subduration, frequency, modulation, gender, culture, voice, volume, radio.alias)
end
@@ -15063,11 +15082,11 @@ function AIRBOSS:SetSRSPilotVoice( Voice, Gender, Culture )
self.PilotRadio.voice = Voice or MSRS.Voices.Microsoft.David
self.PilotRadio.gender = Gender or "male"
self.PilotRadio.culture = Culture or "en-US"
if (not Voice) and self.SRS and self.SRS:GetProvider() == MSRS.Provider.GOOGLE then
self.PilotRadio.voice = MSRS.Voices.Google.Standard.en_US_Standard_J
end
return self
end
@@ -15381,44 +15400,44 @@ function AIRBOSS:MessageToPlayer( playerData, message, sender, receiver, duratio
-- SCHEDULER:New(nil, self.MessageToPlayer, {self, playerData, message, sender, receiver, duration, clear}, delay)
self:ScheduleOnce( delay, self.MessageToPlayer, self, playerData, message, sender, receiver, duration, clear )
else
if not self.SRS then
-- Wait until previous sound finished.
local wait = 0
-- Onboard number to get the attention.
if receiver == playerData.onboard then
-- Which voice over number to use.
if sender and (sender == "LSO" or sender == "MARSHAL" or sender == "AIRBOSS") then
-- User sound of board number.
wait = wait + self:_Number2Sound( playerData, sender, receiver )
end
end
-- Negative.
if string.find( text:lower(), "negative" ) then
local filename = self:_RadioFilename( self.MarshalCall.NEGATIVE, false, "MARSHAL" )
USERSOUND:New( filename ):ToGroup( playerData.group, wait )
wait = wait + self.MarshalCall.NEGATIVE.duration
end
-- Affirm.
if string.find( text:lower(), "affirm" ) then
local filename = self:_RadioFilename( self.MarshalCall.AFFIRMATIVE, false, "MARSHAL" )
USERSOUND:New( filename ):ToGroup( playerData.group, wait )
wait = wait + self.MarshalCall.AFFIRMATIVE.duration
end
-- Roger.
if string.find( text:lower(), "roger" ) then
local filename = self:_RadioFilename( self.MarshalCall.ROGER, false, "MARSHAL" )
USERSOUND:New( filename ):ToGroup( playerData.group, wait )
wait = wait + self.MarshalCall.ROGER.duration
end
-- Play click sound to end message.
if wait > 0 then
local filename = self:_RadioFilename( self.MarshalCall.CLICK )
@@ -15431,7 +15450,7 @@ function AIRBOSS:MessageToPlayer( playerData, message, sender, receiver, duratio
local voice = self.MarshalRadio.voice
local gender = self.MarshalRadio.gender
local culture = self.MarshalRadio.culture
if not sender then sender = "AIRBOSS" end
if string.find(sender,"AIRBOSS" ) then
@@ -17049,7 +17068,7 @@ function AIRBOSS:_RemoveSectionMember( playerData, sectionmember )
return false
end
--- Set all flights within 100 meters to be part of my section.
--- Set all flights within maxsectiondistance meters to be part of my section (default: 100 meters).
-- @param #AIRBOSS self
-- @param #string _unitName Name of the player unit.
function AIRBOSS:_SetSection( _unitName )
@@ -17067,7 +17086,7 @@ function AIRBOSS:_SetSection( _unitName )
local mycoord = _unit:GetCoordinate()
-- Max distance up to which section members are allowed.
local dmax = 100
local dmax = self.maxsectiondistance
-- Check if player is in Marshal or pattern queue already.
local text

View File

@@ -68,7 +68,7 @@ ARMYGROUP = {
--- Army Group version.
-- @field #string version
ARMYGROUP.version="1.0.1"
ARMYGROUP.version="1.0.3"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
@@ -2049,114 +2049,70 @@ end
--- Initialize group parameters. Also initializes waypoints if self.waypoints is nil.
-- @param #ARMYGROUP self
-- @param #table Template Template used to init the group. Default is `self.template`.
-- @param #number Delay Delay in seconds before group is initialized. Default `nil`, *i.e.* instantaneous.
-- @return #ARMYGROUP self
function ARMYGROUP:_InitGroup(Template, Delay)
if Delay and Delay>0 then
self:ScheduleOnce(Delay, ARMYGROUP._InitGroup, self, Template, 0)
else
-- First check if group was already initialized.
if self.groupinitialized then
self:T(self.lid.."WARNING: Group was already initialized! Will NOT do it again!")
return
end
-- Get template of group.
local template=Template or self:_GetTemplate()
-- Ground are always AI.
self.isAI=true
-- Is (template) group late activated.
self.isLateActivated=template.lateActivation
-- Ground groups cannot be uncontrolled.
self.isUncontrolled=false
-- Max speed in km/h.
self.speedMax=self.group:GetSpeedMax()
-- Is group mobile?
if self.speedMax and self.speedMax>3.6 then
self.isMobile=true
else
self.isMobile=false
self.speedMax = 0
end
-- Cruise speed in km/h
self.speedCruise=self.speedMax*0.7
-- Group ammo.
self.ammo=self:GetAmmoTot()
-- Radio parameters from template.
self.radio.On=false -- Radio is always OFF for ground.
self.radio.Freq=133
self.radio.Modu=radio.modulation.AM
-- Set default radio.
self:SetDefaultRadio(self.radio.Freq, self.radio.Modu, self.radio.On)
-- Get current formation from first waypoint.
self.option.Formation=template.route.points[1].action
-- Set default formation to "on road".
self.optionDefault.Formation=ENUMS.Formation.Vehicle.OnRoad
-- First check if group was already initialized.
if self.groupinitialized then
self:T(self.lid.."WARNING: Group was already initialized! Will NOT do it again!")
return
end
self:T(self.lid.."FF Initializing Group")
-- Get template of group.
local template=Template or self:_GetTemplate()
-- Ground are always AI.
self.isAI=true
-- Is (template) group late activated.
self.isLateActivated=template.lateActivation
-- Ground groups cannot be uncontrolled.
self.isUncontrolled=false
-- Max speed in km/h.
self.speedMax=self.group:GetSpeedMax()
-- Is group mobile?
if self.speedMax>3.6 then
if self.speedMax and self.speedMax>3.6 then
self.isMobile=true
else
self.isMobile=false
self.speedMax = 0
end
-- Cruise speed in km/h
self.speedCruise=self.speedMax*0.7
-- Group ammo.
self.ammo=self:GetAmmoTot()
-- Radio parameters from template.
self.radio.On=false -- Radio is always OFF for ground.
self.radio.Freq=133
self.radio.Modu=radio.modulation.AM
-- Set default radio.
self:SetDefaultRadio(self.radio.Freq, self.radio.Modu, self.radio.On)
-- Get current formation from first waypoint.
self.option.Formation=template.route.points[1].action
-- Set default formation to "on road".
self.optionDefault.Formation=ENUMS.Formation.Vehicle.OnRoad
-- Default TACAN off.
self:SetDefaultTACAN(nil, nil, nil, nil, true)
self.tacan=UTILS.DeepCopy(self.tacanDefault)
if not self.tacanDefault then
self:SetDefaultTACAN(nil, nil, nil, nil, true)
end
if not self.tacan then
self.tacan=UTILS.DeepCopy(self.tacanDefault)
end
-- Units of the group.
local units=self.group:GetUnits()
@@ -2177,7 +2133,6 @@ function ARMYGROUP:_InitGroup(Template, Delay)
self:_AddElementByName(unitname)
end
-- Init done.
self.groupinitialized=true
end

View File

@@ -162,6 +162,7 @@
-- @field #number missionRange Mission range in meters. Used by LEGION classes (AIRWING, BRIGADE, ...).
-- @field Core.Point#COORDINATE missionWaypointCoord Mission waypoint coordinate.
-- @field Core.Point#COORDINATE missionEgressCoord Mission egress waypoint coordinate.
-- @field Core.Point#COORDINATE missionIngressCoord Mission Ingress waypoint coordinate.
-- @field #number missionWaypointRadius Random radius in meters.
-- @field #boolean legionReturn If `true`, assets return to their legion (default). If `false`, they will stay alive.
--
@@ -1716,17 +1717,18 @@ end
--- **[AIR]** Create a STRIKE mission. Flight will attack the closest map object to the specified coordinate.
-- @param #AUFTRAG self
-- @param Core.Point#COORDINATE Target The target coordinate. Can also be given as a GROUP, UNIT, STATIC or TARGET object.
-- @param Core.Point#COORDINATE Target The target coordinate. Can also be given as a GROUP, UNIT, STATIC, SET_GROUP, SET_UNIT, SET_STATIC or TARGET object.
-- @param #number Altitude Engage altitude in feet. Default 2000 ft.
-- @param #number EngageWeaponType Which weapon to use. Defaults to auto, ie ENUMS.WeaponFlag.Auto. See ENUMS.WeaponFlag for options.
-- @return #AUFTRAG self
function AUFTRAG:NewSTRIKE(Target, Altitude)
function AUFTRAG:NewSTRIKE(Target, Altitude, EngageWeaponType)
local mission=AUFTRAG:New(AUFTRAG.Type.STRIKE)
mission:_TargetFromObject(Target)
-- DCS Task options:
mission.engageWeaponType=ENUMS.WeaponFlag.Auto
mission.engageWeaponType=EngageWeaponType or ENUMS.WeaponFlag.Auto
mission.engageWeaponExpend=AI.Task.WeaponExpend.ALL
mission.engageAltitude=UTILS.FeetToMeters(Altitude or 2000)
@@ -1747,17 +1749,18 @@ end
--- **[AIR]** Create a BOMBING mission. Flight will drop bombs a specified coordinate.
-- See [DCS task bombing](https://wiki.hoggitworld.com/view/DCS_task_bombing).
-- @param #AUFTRAG self
-- @param Core.Point#COORDINATE Target Target coordinate. Can also be specified as a GROUP, UNIT, STATIC or TARGET object.
-- @param Core.Point#COORDINATE Target Target coordinate. Can also be specified as a GROUP, UNIT, STATIC, SET_GROUP, SET_UNIT, SET_STATIC or TARGET object.
-- @param #number Altitude Engage altitude in feet. Default 25000 ft.
-- @param #number EngageWeaponType Which weapon to use. Defaults to auto, ie ENUMS.WeaponFlag.Auto. See ENUMS.WeaponFlag for options.
-- @return #AUFTRAG self
function AUFTRAG:NewBOMBING(Target, Altitude)
function AUFTRAG:NewBOMBING(Target, Altitude, EngageWeaponType)
local mission=AUFTRAG:New(AUFTRAG.Type.BOMBING)
mission:_TargetFromObject(Target)
-- DCS task options:
mission.engageWeaponType=ENUMS.WeaponFlag.Auto
mission.engageWeaponType=EngageWeaponType or ENUMS.WeaponFlag.Auto
mission.engageWeaponExpend=AI.Task.WeaponExpend.ALL
mission.engageAltitude=UTILS.FeetToMeters(Altitude or 25000)
@@ -1819,7 +1822,7 @@ end
--- **[AIR]** Create a BOMBRUNWAY mission.
-- @param #AUFTRAG self
-- @param Wrapper.Airbase#AIRBASE Airdrome The airbase to bomb. This must be an airdrome (not a FARP or ship) as these to not have a runway.
-- @param Wrapper.Airbase#AIRBASE Airdrome The airbase to bomb. This must be an airdrome (not a FARP or ship) as these do not have a runway.
-- @param #number Altitude Engage altitude in feet. Default 25000 ft.
-- @return #AUFTRAG self
function AUFTRAG:NewBOMBRUNWAY(Airdrome, Altitude)
@@ -2155,7 +2158,11 @@ end
]]
--- **[GROUND, NAVAL]** Create an ARTY mission.
--- **[GROUND, NAVAL]** Create an ARTY mission ("Fire at point" task).
--
-- If the group has more than one weapon type supporting the "Fire at point" task, the employed weapon type can be set via the `AUFTRAG:SetWeaponType()` function.
--
-- **Note** that it is recommended to set the weapon range via the `OPSGROUP:AddWeaponRange()` function as this cannot be retrieved from the DCS API.
-- @param #AUFTRAG self
-- @param Core.Point#COORDINATE Target Center of the firing solution.
-- @param #number Nshots Number of shots to be fired. Default `#nil`.
@@ -2178,6 +2185,7 @@ function AUFTRAG:NewARTY(Target, Nshots, Radius, Altitude)
mission.optionAlarm=0
mission.missionFraction=0.0
mission.missionWaypointRadius=0.0
-- Evaluate after 8 min.
mission.dTevaluate=8*60
@@ -2302,7 +2310,7 @@ function AUFTRAG:NewCAPTUREZONE(OpsZone, Coalition, Speed, Altitude, Formation)
params.formation=Formation or "Off Road"
params.zone=mission:GetObjective()
params.altitude=mission.missionAltitude
params.speed=mission.missionSpeed
params.speed=mission.missionSpeed and UTILS.KmphToMps(mission.missionSpeed) or nil
mission.DCStask.params=params
@@ -2352,7 +2360,7 @@ function AUFTRAG:NewGROUNDATTACK(Target, Speed, Formation)
mission.DCStask=mission:GetDCSMissionTask()
mission.DCStask.params.speed=Speed
mission.DCStask.params.speed=mission.missionSpeed and UTILS.KmphToMps(mission.missionSpeed) or nil
mission.DCStask.params.formation=Formation or ENUMS.Formation.Vehicle.Vee
return mission
@@ -3481,7 +3489,7 @@ end
--- Set Rules of Engagement (ROE) for this mission.
-- @param #AUFTRAG self
-- @param #string roe Mission ROE.
-- @param #number roe Mission ROE, e.g. `ENUMS.ROE.ReturnFire` (whiche equals 3)
-- @return #AUFTRAG self
function AUFTRAG:SetROE(roe)
@@ -3493,7 +3501,7 @@ end
--- Set Reaction on Threat (ROT) for this mission.
-- @param #AUFTRAG self
-- @param #string rot Mission ROT.
-- @param #number rot Mission ROT, e.g. `ENUMS.ROT.NoReaction` (whiche equals 0)
-- @return #AUFTRAG self
function AUFTRAG:SetROT(rot)
@@ -4657,6 +4665,16 @@ function AUFTRAG:SetGroupWaypointCoordinate(opsgroup, coordinate)
return self
end
--- [Air] Set mission (ingress) waypoint coordinate for FLIGHT group.
-- @param #AUFTRAG self
-- @param Core.Point#COORDINATE coordinate Waypoint Coordinate.
-- @return #AUFTRAG self
function AUFTRAG:SetIngressCoordinate(coordinate)
self.missionIngressCoord = coordinate
self.missionIngressCoordAlt = UTILS.MetersToFeet(coordinate.y) or 10000
return self
end
--- Get mission (ingress) waypoint coordinate of OPS group
-- @param #AUFTRAG self
-- @param Ops.OpsGroup#OPSGROUP opsgroup The OPS group.
@@ -5712,7 +5730,7 @@ function AUFTRAG:GetMissionTypesText(MissionTypes)
return text
end
--- Set the mission waypoint coordinate where the mission is executed. Note that altitude is set via `:SetMissionAltitude`.
--- [NON-AIR] Set the mission waypoint coordinate from where the mission is executed. Note that altitude is set via `:SetMissionAltitude`.
-- @param #AUFTRAG self
-- @param Core.Point#COORDINATE Coordinate Coordinate where the mission is executed.
-- @return #AUFTRAG self
@@ -5740,8 +5758,9 @@ end
-- @param #AUFTRAG self
-- @param Core.Point#COORDINATE Coordinate Egrees coordinate.
-- @param #number Altitude (Optional) Altitude in feet. Default is y component of coordinate.
-- @param #number Speed (Optional) Speed in knots to reach this waypoint. Defaults to mission speed.
-- @return #AUFTRAG self
function AUFTRAG:SetMissionEgressCoord(Coordinate, Altitude)
function AUFTRAG:SetMissionEgressCoord(Coordinate, Altitude, Speed)
-- Obviously a zone was passed. We get the coordinate.
if Coordinate:IsInstanceOf("ZONE_BASE") then
@@ -5752,7 +5771,64 @@ function AUFTRAG:SetMissionEgressCoord(Coordinate, Altitude)
if Altitude then
self.missionEgressCoord.y=UTILS.FeetToMeters(Altitude)
self.missionEgressCoordAlt = UTILS.FeetToMeters(Altitude)
end
self.missionEgressCoordSpeed=Speed and Speed or nil
return self
end
--- [Air] Set the mission ingress coordinate. This is the coordinate where the assigned group will fly before the actual mission coordinate.
-- @param #AUFTRAG self
-- @param Core.Point#COORDINATE Coordinate Ingrees coordinate.
-- @param #number Altitude (Optional) Altitude in feet. Default is y component of coordinate.
-- @param #number Speed (Optional) Speed in knots to reach this waypoint. Defaults to mission speed.
-- @return #AUFTRAG self
function AUFTRAG:SetMissionIngressCoord(Coordinate, Altitude, Speed)
-- Obviously a zone was passed. We get the coordinate.
if Coordinate:IsInstanceOf("ZONE_BASE") then
Coordinate=Coordinate:GetCoordinate()
end
self.missionIngressCoord=Coordinate
if Altitude then
self.missionIngressCoord.y=UTILS.FeetToMeters(Altitude)
self.missionIngressCoordAlt = UTILS.FeetToMeters(Altitude or 10000)
end
self.missionIngressCoordSpeed=Speed and Speed or nil
return self
end
--- [Air] Set the mission holding coordinate. This is the coordinate where the assigned group will fly before the actual mission execution starts. Do not forget to add a push condition, too!
-- @param #AUFTRAG self
-- @param Core.Point#COORDINATE Coordinate Holding coordinate.
-- @param #number Altitude (Optional) Altitude in feet. Default is y component of coordinate.
-- @param #number Speed (Optional) Speed in knots to reach this waypoint and hold there. Defaults to mission speed.
-- @param #number Duration (Optional) Duration in seconds on how long to hold, defaults to 15 minutes. Mission continues if either a push condition is met or the time is up.
-- @return #AUFTRAG self
function AUFTRAG:SetMissionHoldingCoord(Coordinate, Altitude, Speed, Duration)
-- Obviously a zone was passed. We get the coordinate.
if Coordinate:IsInstanceOf("ZONE_BASE") then
Coordinate=Coordinate:GetCoordinate()
end
self.missionHoldingCoord=Coordinate
self.missionHoldingDuration=Duration or 900
if Altitude then
self.missionHoldingCoord.y=UTILS.FeetToMeters(Altitude)
self.missionHoldingCoordAlt = UTILS.FeetToMeters(Altitude or 10000)
end
self.missionHoldingCoordSpeed=Speed and Speed or nil
return self
end
--- Get the mission egress coordinate if this was defined.
@@ -5762,6 +5838,20 @@ function AUFTRAG:GetMissionEgressCoord()
return self.missionEgressCoord
end
--- Get the mission ingress coordinate if this was defined.
-- @param #AUFTRAG self
-- @return Core.Point#COORDINATE Coordinate Coordinate or nil.
function AUFTRAG:GetMissionIngressCoord()
return self.missionIngressCoord
end
--- Get the mission holding coordinate if this was defined.
-- @param #AUFTRAG self
-- @return Core.Point#COORDINATE Coordinate Coordinate or nil.
function AUFTRAG:GetMissionHoldingCoord()
return self.missionHoldingCoord
end
--- Get coordinate which was set as mission waypoint coordinate.
-- @param #AUFTRAG self
-- @return Core.Point#COORDINATE Coordinate where the mission is executed or `#nil`.
@@ -5796,10 +5886,27 @@ function AUFTRAG:GetMissionWaypointCoord(group, randomradius, surfacetypes)
end
return coord
end
local coord=group:GetCoordinate()
-- Check if an ingress or holding coord has been explicitly set.
if self.missionHoldingCoord then
coord=self.missionHoldingCoord
if self.missionHoldingCoorddAlt then
coord:SetAltitude(self.missionHoldingCoordAlt, true)
end
end
if self.missionIngressCoord then
coord=self.missionIngressCoord
if self.missionIngressCoordAlt then
coord:SetAltitude(self.missionIngressCoordAlt, true)
end
end
-- Create waypoint coordinate half way between us and the target.
local waypointcoord=COORDINATE:New(0,0,0)
local coord=group:GetCoordinate()
if coord then
waypointcoord=coord:GetIntermediateCoordinate(self:GetTargetCoordinate(), self.missionFraction)
else
@@ -6001,10 +6108,13 @@ function AUFTRAG:GetDCSMissionTask()
-- BOMBING Mission --
---------------------
local DCStask=CONTROLLABLE.TaskBombing(nil, self:GetTargetVec2(), self.engageAsGroup, self.engageWeaponExpend, self.engageQuantity, self.engageDirection, self.engageAltitude, self.engageWeaponType, Divebomb)
local coords = self.engageTarget:GetCoordinates()
for _, coord in pairs(coords) do
local DCStask = CONTROLLABLE.TaskBombing(nil, coord:GetVec2(), self.engageAsGroup, self.engageWeaponExpend, self.engageQuantity, self.engageDirection, self.engageAltitude, self.engageWeaponType)
table.insert(DCStasks, DCStask)
end
table.insert(DCStasks, DCStask)
elseif self.type==AUFTRAG.Type.STRAFING then
----------------------
@@ -6099,7 +6209,7 @@ function AUFTRAG:GetDCSMissionTask()
local param={}
param.zone=self:GetObjective()
param.altitude=self.missionAltitude
param.speed=self.missionSpeed
param.speed=self.missionSpeed and UTILS.KmphToMps(self.missionSpeed) or nil
DCStask.params=param
@@ -6179,7 +6289,7 @@ function AUFTRAG:GetDCSMissionTask()
local param={}
param.target=self.engageTarget
param.altitude=self.missionAltitude
param.speed=self.missionSpeed
param.speed=self.missionSpeed and UTILS.KmphToMps(self.missionSpeed) or nil
param.lastindex=nil
DCStask.params=param
@@ -6204,9 +6314,12 @@ function AUFTRAG:GetDCSMissionTask()
-- STRIKE Mission --
--------------------
local DCStask=CONTROLLABLE.TaskAttackMapObject(nil, self:GetTargetVec2(), self.engageAsGroup, self.engageWeaponExpend, self.engageQuantity, self.engageDirection, self.engageAltitude, self.engageWeaponType)
local coords = self.engageTarget:GetCoordinates()
for _, coord in pairs(coords) do
local DCStask=CONTROLLABLE.TaskAttackMapObject(nil, coord:GetVec2(), self.engageAsGroup, self.engageWeaponExpend, self.engageQuantity, self.engageDirection, self.engageAltitude, self.engageWeaponType)
table.insert(DCStasks, DCStask)
table.insert(DCStasks, DCStask)
end
elseif self.type==AUFTRAG.Type.TANKER or self.type==AUFTRAG.Type.RECOVERYTANKER then
@@ -6352,7 +6465,7 @@ function AUFTRAG:GetDCSMissionTask()
local param={}
param.zone=self:GetObjective()
param.altitude=self.missionAltitude
param.speed=self.missionSpeed
param.speed=self.missionSpeed and UTILS.KmphToMps(self.missionSpeed) or nil
DCStask.params=param
@@ -6388,7 +6501,7 @@ function AUFTRAG:GetDCSMissionTask()
local param={}
param.zone=self:GetObjective()
param.altitude=self.missionAltitude
param.speed=self.missionSpeed
param.speed=self.missionSpeed and UTILS.KmphToMps(self.missionSpeed) or nil
DCStask.params=param
@@ -6408,7 +6521,7 @@ function AUFTRAG:GetDCSMissionTask()
local param={}
param.target=self:GetTargetData()
param.action="Wedge"
param.speed=self.missionSpeed
param.speed=self.missionSpeed and UTILS.KmphToMps(self.missionSpeed) or nil
DCStask.params=param

View File

@@ -17,7 +17,7 @@
-- ===
--
-- ### Author: **applevangelist**
-- @date Last Update July 2024
-- @date Last Update Jan 2025
-- @module Ops.AWACS
-- @image OPS_AWACS.jpg
@@ -122,6 +122,7 @@ do
-- @field #number TacticalModulation
-- @field #number TacticalInterval
-- @field Core.Set#SET_GROUP DetectionSet
-- @field #number MaxMissionRange
-- @extends Core.Fsm#FSM
@@ -183,7 +184,7 @@ do
--
-- Add Escorts Squad (recommended, optional)
--
-- local Squad_Two = SQUADRON:New("Escorts",4,"Escorts North")
-- local Squad_Two = SQUADRON:New("Escorts",4,"Escorts North") -- taking a template with 2 planes here, will result in a group of 2 escorts which can fly in formation escorting the AWACS.
-- Squad_Two:AddMissionCapability({AUFTRAG.Type.ESCORT})
-- Squad_Two:SetFuelLowRefuel(true)
-- Squad_Two:SetFuelLowThreshold(0.3)
@@ -231,8 +232,8 @@ do
-- -- set up in the mission editor with a late activated helo named "Rock#ZONE_POLYGON". Note this also sets the BullsEye to be referenced as "Rock".
-- -- The CAP station zone is called "Fremont". We will be on 255 AM.
-- local testawacs = AWACS:New("AWACS North",AwacsAW,"blue",AIRBASE.Caucasus.Kutaisi,"Awacs Orbit",ZONE:FindByName("Rock"),"Fremont",255,radio.modulation.AM )
-- -- set two escorts
-- testawacs:SetEscort(2)
-- -- set one escort group; this example has two units in the template group, so they can fly a nice formation.
-- testawacs:SetEscort(1,ENUMS.Formation.FixedWing.FingerFour.Group,{x=-500,y=50,z=500},45)
-- -- Callsign will be "Focus". We'll be a Angels 30, doing 300 knots, orbit leg to 88deg with a length of 25nm.
-- testawacs:SetAwacsDetails(CALLSIGN.AWACS.Focus,1,30,300,88,25)
-- -- Set up SRS on port 5010 - change the below to your path and port
@@ -508,7 +509,7 @@ do
-- @field #AWACS
AWACS = {
ClassName = "AWACS", -- #string
version = "0.2.65", -- #string
version = "0.2.71", -- #string
lid = "", -- #string
coalition = coalition.side.BLUE, -- #number
coalitiontxt = "blue", -- #string
@@ -605,6 +606,7 @@ AWACS = {
TacticalModulation = radio.modulation.AM,
TacticalInterval = 120,
DetectionSet = nil,
MaxMissionRange = 125,
}
---
@@ -1572,6 +1574,15 @@ function AWACS:SetLocale(Locale)
return self
end
--- [User] Set the max mission range flights can be away from their home base.
-- @param #AWACS self
-- @param #number NM Distance in nautical miles
-- @return #AWACS self
function AWACS:SetMaxMissionRange(NM)
self.MaxMissionRange = NM or 125
return self
end
--- [User] Add additional frequency and modulation for AWACS SRS output.
-- @param #AWACS self
-- @param #number Frequency The frequency to add, e.g. 132.5
@@ -1762,7 +1773,7 @@ function AWACS:_EventHandler(EventData)
end
end
if Event.id == EVENTS.PlayerLeaveUnit then --player left unit
if Event.id == EVENTS.PlayerLeaveUnit and Event.IniGroupName then --player left unit
-- check known player?
self:T("Player group left unit: " .. Event.IniGroupName)
self:T("Player name left: " .. Event.IniPlayerName)
@@ -2158,9 +2169,12 @@ end
--- [User] Set AWACS Escorts Template
-- @param #AWACS self
-- @param #number EscortNumber Number of fighther planes to accompany this AWACS. 0 or nil means no escorts.
-- @param #number EscortNumber Number of fighther plane GROUPs to accompany this AWACS. 0 or nil means no escorts. If you want >1 plane in an escort group, you can either set the respective squadron grouping to the desired number, or use a template for escorts with >1 unit.
-- @param #number Formation Formation the escort should take (if more than one plane), e.g. `ENUMS.Formation.FixedWing.FingerFour.Group`. Formation is used on GROUP level, multiple groups of one unit will NOT conform to this formation.
-- @param #table OffsetVector Offset the escorts should fly behind the AWACS, given as table, distance in meters, e.g. `{x=-500,y=0,z=500}` - 500m behind (negative value) and to the right (negative for left), no vertical separation (positive over, negative under the AWACS flight). For multiple groups, the vectors will be slightly changed to avoid collisions.
-- @param #number EscortEngageMaxDistance Escorts engage air targets max this NM away, defaults to 45NM.
-- @return #AWACS self
function AWACS:SetEscort(EscortNumber)
function AWACS:SetEscort(EscortNumber,Formation,OffsetVector,EscortEngageMaxDistance)
self:T(self.lid.."SetEscort")
if EscortNumber and EscortNumber > 0 then
self.HasEscorts = true
@@ -2169,6 +2183,9 @@ function AWACS:SetEscort(EscortNumber)
self.HasEscorts = false
self.EscortNumber = 0
end
self.EscortFormation = Formation
self.OffsetVec = OffsetVector or {x=500,y=100,z=500}
self.EscortEngageMaxDistance = EscortEngageMaxDistance or 45
return self
end
@@ -2223,11 +2240,26 @@ function AWACS:_StartEscorts(Shiftchange)
local group = AwacsFG:GetGroup()
local timeonstation = (self.EscortsTimeOnStation + self.ShiftChangeTime) * 3600 -- hours to seconds
local OffsetX = 500
local OffsetY = 500
local OffsetZ = 500
if self.OffsetVec then
OffsetX = self.OffsetVec.x or 500
OffsetY = self.OffsetVec.y or 500
OffsetZ = self.OffsetVec.z or 500
end
for i=1,self.EscortNumber do
-- every
local escort = AUFTRAG:NewESCORT(group, {x= -100*((i + (i%2))/2), y=0, z=(100 + 100*((i + (i%2))/2))*(-1)^i},45,{"Air"})
escort:SetRequiredAssets(1)
-- every
local escort = AUFTRAG:NewESCORT(group, {x= OffsetX*((i + (i%2))/2), y=OffsetY*((i + (i%2))/2), z=(OffsetZ + OffsetZ*((i + (i%2))/2))*(-1)^i},self.EscortEngageMaxDistance,{"Air"})
--local escort = AUFTRAG:NewESCORT(group,self.OffsetVec,self.EscortEngageMaxDistance,{"Air"})
--escort:SetRequiredAssets(self.EscortNumber)
escort:SetTime(nil,timeonstation)
if self.Escortformation then
escort:SetFormation(self.Escortformation)
end
escort:SetMissionRange(self.MaxMissionRange)
self.AirWing:AddMission(escort)
self.CatchAllMissions[#self.CatchAllMissions+1] = escort
@@ -2434,7 +2466,7 @@ function AWACS:_GetCallSign(Group,GID, IsPlayer)
local callsign = "Ghost 1"
if Group and Group:IsAlive() then
callsign = Group:GetCustomCallSign(self.callsignshort,self.keepnumber,self.callsignTranslations)
callsign = Group:GetCustomCallSign(self.callsignshort,self.keepnumber,self.callsignTranslations,self.callsignCustomFunc,self.callsignCustomArgs)
end
return callsign
end
@@ -2443,10 +2475,12 @@ end
-- @param #AWACS self
-- @param #boolean ShortCallsign If true, only call out the major flight number
-- @param #boolean Keepnumber If true, keep the **customized callsign** in the #GROUP name as-is, no amendments or numbers.
-- @param #table CallsignTranslations (optional) Table to translate between DCS standard callsigns and bespoke ones. Does not apply if using customized
-- @param #table CallsignTranslations (Optional) Table to translate between DCS standard callsigns and bespoke ones. Does not apply if using customized.
-- callsigns from playername or group name.
-- @param #func CallsignCustomFunc (Optional) For player names only(!). If given, this function will return the callsign. Needs to take the groupname and the playername as first two arguments.
-- @param #arg ... (Optional) Comma separated arguments to add to the custom function call after groupname and playername.
-- @return #AWACS self
function AWACS:SetCallSignOptions(ShortCallsign,Keepnumber,CallsignTranslations)
function AWACS:SetCallSignOptions(ShortCallsign,Keepnumber,CallsignTranslations,CallsignCustomFunc,...)
if not ShortCallsign or ShortCallsign == false then
self.callsignshort = false
else
@@ -2454,6 +2488,8 @@ function AWACS:SetCallSignOptions(ShortCallsign,Keepnumber,CallsignTranslations)
end
self.keepnumber = Keepnumber or false
self.callsignTranslations = CallsignTranslations
self.callsignCustomFunc = CallsignCustomFunc
self.callsignCustomArgs = arg or {}
return self
end
@@ -3626,7 +3662,7 @@ function AWACS:_CheckIn(Group)
managedgroup.LastTasking = timer.getTime()
GID = managedgroup.GID
self.ManagedGrps[self.ManagedGrpID]=managedgroup
self.ManagedGrps[self.ManagedGrpID]=managedgroup
local alphacheckbulls = self:_ToStringBULLS(Group:GetCoordinate())
local alphacheckbullstts = self:_ToStringBULLS(Group:GetCoordinate(),false,true)
@@ -3893,6 +3929,12 @@ function AWACS:_SetClientMenus()
checkin = checkin,
}
self.clientmenus:Push(menus,cgrpname)
-- catch errors - when this entry is built we should NOT have a managed entry
local GID,hasentry = self:_GetManagedGrpID(cgrp)
if hasentry then
-- this user is checked in but has the check in entry ... not good.
self:_CheckOut(cgrp,GID,true)
end
end
end
else
@@ -5739,7 +5781,7 @@ function AWACS:_AssignPilotToTarget(Pilots,Targets)
local intercept = AUFTRAG:NewINTERCEPT(Target.Target)
intercept:SetWeaponExpend(AI.Task.WeaponExpend.ALL)
intercept:SetWeaponType(ENUMS.WeaponFlag.Auto)
intercept:SetMissionRange(self.MaxMissionRange)
-- TODO
-- now this is going to be interesting...
-- Check if the target left the "hot" area or is dead already
@@ -5787,6 +5829,7 @@ function AWACS:_AssignPilotToTarget(Pilots,Targets)
AnchorSpeed = UTILS.KnotsToAltKIAS(AnchorSpeed,Angels)
local Anchor = self.AnchorStacks:ReadByPointer(Pilot.AnchorStackNo) -- #AWACS.AnchorData
local capauftrag = AUFTRAG:NewCAP(Anchor.StationZone,Angels,AnchorSpeed,Anchor.StationZoneCoordinate,0,15,{})
capauftrag:SetMissionRange(self.MaxMissionRange)
capauftrag:SetTime(nil,((self.CAPTimeOnStation*3600)+(15*60)))
Pilot.FlightGroup:AddMission(capauftrag)
@@ -5904,6 +5947,8 @@ function AWACS:onafterStart(From, Event, To)
-- set up the AWACS and let it orbit
local AwacsAW = self.AirWing -- Ops.Airwing#AIRWING
local mission = AUFTRAG:NewORBIT_RACETRACK(self.OrbitZone:GetCoordinate(),self.AwacsAngels*1000,self.Speed,self.Heading,self.Leg)
mission:SetMissionRange(self.MaxMissionRange)
mission:SetRequiredAttribute({ GROUP.Attribute.AIR_AWACS }) -- prefered plane type, thanks to Heart8reaker
local timeonstation = (self.AwacsTimeOnStation + self.ShiftChangeTime) * 3600
mission:SetTime(nil,timeonstation)
self.CatchAllMissions[#self.CatchAllMissions+1] = mission
@@ -6046,6 +6091,7 @@ function AWACS:_CheckAwacsStatus()
end
end
end
--------------------------------
-- AWACS
--------------------------------
@@ -6194,12 +6240,13 @@ function AWACS:_CheckAwacsStatus()
report:Add("====================")
local RESMission
-- Check for replacement mission - if any
if self.ShiftChangeEscortsFlag and self.ShiftChangeEscortsRequested then -- Ops.Auftrag#AUFTRAG
ESmission = self.EscortMissionReplacement[i]
local esstatus = ESmission:GetState()
local ESmissiontime = (timer.getTime() - self.EscortsTimeStamp)
local ESTOSLeft = UTILS.Round((((self.EscortsTimeOnStation+self.ShiftChangeTime)*3600) - ESmissiontime),0) -- seconds
RESMission = self.EscortMissionReplacement[i]
local esstatus = RESMission:GetState()
local RESMissiontime = (timer.getTime() - self.EscortsTimeStamp)
local ESTOSLeft = UTILS.Round((((self.EscortsTimeOnStation+self.ShiftChangeTime)*3600) - RESMissiontime),0) -- seconds
ESTOSLeft = UTILS.Round(ESTOSLeft/60,0) -- minutes
local ChangeTime = UTILS.Round(((self.ShiftChangeTime * 3600)/60),0)
@@ -6207,7 +6254,7 @@ function AWACS:_CheckAwacsStatus()
report:Add(string.format("Auftrag Status: %s",esstatus))
report:Add(string.format("TOS Left: %d min",ESTOSLeft))
local OpsGroups = ESmission:GetOpsGroups()
local OpsGroups = RESMission:GetOpsGroups()
local OpsGroup = self:_GetAliveOpsGroupFromTable(OpsGroups) -- Ops.OpsGroup#OPSGROUP
if OpsGroup then
local OpsName = OpsGroup:GetName() or "Unknown"
@@ -6219,13 +6266,13 @@ function AWACS:_CheckAwacsStatus()
report:Add("***** Cannot obtain (yet) this missions OpsGroup!")
end
if ESmission:IsExecuting() then
if RESMission and RESMission:IsExecuting() then
-- make the actual change in the queue
self.ShiftChangeEscortsFlag = false
self.ShiftChangeEscortsRequested = false
-- cancel old mission
if ESmission and ESmission:IsNotOver() then
ESmission:Cancel()
ESmission:__Cancel(1)
end
self.EscortMission[i] = self.EscortMissionReplacement[i]
self.EscortMissionReplacement[i] = nil
@@ -6477,6 +6524,7 @@ function AWACS:onafterAssignedAnchor(From, Event, To, GID, Anchor, AnchorStackNo
if auftragtype == AUFTRAG.Type.ALERT5 then
-- all correct
local capauftrag = AUFTRAG:NewCAP(Anchor.StationZone,Angels*1000,AnchorSpeed,Anchor.StationZone:GetCoordinate(),0,15,{})
capauftrag:SetMissionRange(self.MaxMissionRange)
capauftrag:SetTime(nil,((self.CAPTimeOnStation*3600)+(15*60)))
capauftrag:AddAsset(managedgroup.FlightGroup)
self.CatchAllMissions[#self.CatchAllMissions+1] = capauftrag
@@ -6840,7 +6888,8 @@ function AWACS:onafterAwacsShiftChange(From,Event,To)
self.CatchAllMissions[#self.CatchAllMissions+1] = mission
local timeonstation = (self.AwacsTimeOnStation + self.ShiftChangeTime) * 3600
mission:SetTime(nil,timeonstation)
mission:SetMissionRange(self.MaxMissionRange)
AwacsAW:AddMission(mission)
self.AwacsMissionReplacement = mission

View File

@@ -31,7 +31,7 @@
-- @image OPS_CSAR.jpg
---
-- Last Update Sep 2024
-- Last Update May 2025
-------------------------------------------------------------------------
--- **CSAR** class, extends Core.Base#BASE, Core.Fsm#FSM
@@ -92,7 +92,7 @@
-- mycsar.immortalcrew = true -- Set to true to make wounded crew immortal.
-- mycsar.invisiblecrew = false -- Set to true to make wounded crew insvisible.
-- mycsar.loadDistance = 75 -- configure distance for pilots to get into helicopter in meters.
-- mycsar.mashprefix = {"MASH"} -- prefixes of #GROUP objects used as MASHes.
-- mycsar.mashprefix = {"MASH"} -- prefixes of #GROUP objects used as MASHes. Will also try to add ZONE and STATIC objects with this prefix once at startup.
-- mycsar.max_units = 6 -- max number of pilots that can be carried if #CSAR.AircraftType is undefined.
-- mycsar.messageTime = 15 -- Time to show messages for in seconds. Doubled for long messages.
-- mycsar.radioSound = "beacon.ogg" -- the name of the sound file to use for the pilots\' radio beacons.
@@ -308,12 +308,12 @@ CSAR.AircraftType["AH-64D_BLK_II"] = 2
CSAR.AircraftType["Bronco-OV-10A"] = 2
CSAR.AircraftType["MH-60R"] = 10
CSAR.AircraftType["OH-6A"] = 2
CSAR.AircraftType["OH-58D"] = 2
CSAR.AircraftType["OH58D"] = 2
CSAR.AircraftType["CH-47Fbl1"] = 31
--- CSAR class version.
-- @field #string version
CSAR.version="1.0.29"
CSAR.version="1.0.32"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ToDo list
@@ -468,7 +468,7 @@ function CSAR:New(Coalition, Template, Alias)
-- added 1.0.15
self.allowbronco = false -- set to true to use the Bronco mod as a CSAR plane
self.ADFRadioPwr = 1000
self.ADFRadioPwr = 500
-- added 1.0.16
self.PilotWeight = 80
@@ -809,6 +809,8 @@ end
-- @param #boolean noMessage
-- @param #string _description Description
-- @param #boolean forcedesc Use the description only for the pilot track entry
-- @return Wrapper.Group#GROUP PilotInField Pilot GROUP object
-- @return #string AliasName Alias display name
function CSAR:_AddCsar(_coalition , _country, _point, _typeName, _unitName, _playerName, _freq, noMessage, _description, forcedesc )
self:T(self.lid .. " _AddCsar")
self:T({_coalition , _country, _point, _typeName, _unitName, _playerName, _freq, noMessage, _description})
@@ -841,9 +843,9 @@ function CSAR:_AddCsar(_coalition , _country, _point, _typeName, _unitName, _pla
local BeaconName
if _playerName then
BeaconName = _unitName..math.random(1,10000)
elseif _unitName then
BeaconName = _playerName..math.random(1,10000)
elseif _unitName then
BeaconName = _unitName..math.random(1,10000)
else
BeaconName = "Ghost-1-1"..math.random(1,10000)
end
@@ -878,7 +880,7 @@ function CSAR:_AddCsar(_coalition , _country, _point, _typeName, _unitName, _pla
self:_InitSARForPilot(_spawnedGroup, _unitName, _freq, noMessage, _playerName) --shagrat use unitName to have the aircraft callsign / descriptive "name" etc.
return self
return _spawnedGroup, _alias
end
--- (Internal) Function to add a CSAR object into the scene at a zone coordinate. For mission designers wanting to add e.g. PoWs to the scene.
@@ -1829,8 +1831,9 @@ end
--- (Internal) Function to get string of a group\'s position.
-- @param #CSAR self
-- @param Wrapper.Controllable#CONTROLLABLE _woundedGroup Group or Unit object.
-- @param Wrapper.Unit#UNIT _Unit Requesting helo pilot unit
-- @return #string Coordinates as Text
function CSAR:_GetPositionOfWounded(_woundedGroup)
function CSAR:_GetPositionOfWounded(_woundedGroup,_Unit)
self:T(self.lid .. " _GetPositionOfWounded")
local _coordinate = _woundedGroup:GetCoordinate()
local _coordinatesText = "None"
@@ -1845,6 +1848,26 @@ function CSAR:_GetPositionOfWounded(_woundedGroup)
_coordinatesText = _coordinate:ToStringBULLS(self.coalition)
end
end
if _Unit and _Unit:GetPlayerName() then
local playername = _Unit:GetPlayerName()
if playername then
local settings = _DATABASE:GetPlayerSettings(playername) or _SETTINGS
if settings then
self:T("Get Settings ok!")
if settings:IsA2G_MGRS() then
_coordinatesText = _coordinate:ToStringMGRS(settings)
elseif settings:IsA2G_LL_DMS() then
_coordinatesText = _coordinate:ToStringLLDMS(settings)
elseif settings:IsA2G_LL_DDM() then
_coordinatesText = _coordinate:ToStringLLDDM(settings)
elseif settings:IsA2G_BR() then
-- attention this is the distance from the ASKING unit to target, not from RECCE to target!
local startcoordinate = _Unit:GetCoordinate()
_coordinatesText = _coordinate:ToStringBR(startcoordinate,settings)
end
end
end
end
return _coordinatesText
end
@@ -1870,13 +1893,17 @@ function CSAR:_DisplayActiveSAR(_unitName)
self:T({Table=_value})
local _woundedGroup = _value.group
if _woundedGroup and _value.alive then
local _coordinatesText = self:_GetPositionOfWounded(_woundedGroup)
local _coordinatesText = self:_GetPositionOfWounded(_woundedGroup,_heli)
local _helicoord = _heli:GetCoordinate()
local _woundcoord = _woundedGroup:GetCoordinate()
local _distance = self:_GetDistance(_helicoord, _woundcoord)
self:T({_distance = _distance})
local distancetext = ""
if _SETTINGS:IsImperial() then
local settings = _SETTINGS
if _heli:GetPlayerName() then
settings = _DATABASE:GetPlayerSettings(_heli:GetPlayerName()) or _SETTINGS
end
if settings:IsImperial() then
distancetext = string.format("%.1fnm",UTILS.MetersToNM(_distance))
else
distancetext = string.format("%.1fkm", _distance/1000.0)
@@ -2093,7 +2120,11 @@ end
function CSAR:_GetClosestMASH(_heli)
self:T(self.lid .. " _GetClosestMASH")
local _mashset = self.mash -- Core.Set#SET_GROUP
local _mashes = _mashset:GetSetObjects() -- #table
local MashSets = {}
--local _mashes = _mashset.Set-- #table
table.insert(MashSets,_mashset.Set)
table.insert(MashSets,self.zonemashes.Set)
table.insert(MashSets,self.staticmashes.Set)
local _shortestDistance = -1
local _distance = 0
local _helicoord = _heli:GetCoordinate()
@@ -2124,14 +2155,19 @@ function CSAR:_GetClosestMASH(_heli)
_shortestDistance = distance
end
for _, _mashUnit in pairs(_mashes) do
if _mashUnit and _mashUnit:IsAlive() then
local _mashcoord = _mashUnit:GetCoordinate()
_distance = self:_GetDistance(_helicoord, _mashcoord)
if _distance ~= nil and (_shortestDistance == -1 or _distance < _shortestDistance) then
_shortestDistance = _distance
end
end
for _,_mashes in pairs(MashSets) do
for _, _mashUnit in pairs(_mashes or {}) do
local _mashcoord
if _mashUnit and (not _mashUnit:IsInstanceOf("ZONE_BASE")) and _mashUnit:IsAlive() then
_mashcoord = _mashUnit:GetCoordinate()
elseif _mashUnit and _mashUnit:IsInstanceOf("ZONE_BASE") then
_mashcoord = _mashUnit:GetCoordinate()
end
_distance = self:_GetDistance(_helicoord, _mashcoord)
if _distance ~= nil and (_shortestDistance == -1 or _distance < _shortestDistance) then
_shortestDistance = _distance
end
end
end
if _shortestDistance ~= -1 then
@@ -2139,6 +2175,7 @@ function CSAR:_GetClosestMASH(_heli)
else
return -1
end
end
--- (Internal) Display onboarded rescued pilots.
@@ -2296,9 +2333,9 @@ end
-- @param #CSAR self
-- @param Wrapper.Group#GROUP _group Group #GROUP object.
-- @param #number _freq Frequency to use
-- @param #string _name Beacon Name to use
-- @param #string BeaconName Beacon Name to use
-- @return #CSAR self
function CSAR:_AddBeaconToGroup(_group, _freq, _name)
function CSAR:_AddBeaconToGroup(_group, _freq, BeaconName)
self:T(self.lid .. " _AddBeaconToGroup")
if self.CreateRadioBeacons == false then return end
local _group = _group
@@ -2319,10 +2356,11 @@ function CSAR:_AddBeaconToGroup(_group, _freq, _name)
if _radioUnit then
local name = _radioUnit:GetName()
local Frequency = _freq -- Freq in Hertz
local name = _radioUnit:GetName()
--local name = _radioUnit:GetName()
local Sound = "l10n/DEFAULT/"..self.radioSound
local vec3 = _radioUnit:GetVec3() or _radioUnit:GetPositionVec3() or {x=0,y=0,z=0}
trigger.action.radioTransmission(Sound, vec3, 0, false, Frequency, self.ADFRadioPwr or 1000,_name) -- Beacon in MP only runs for exactly 30secs straight
self:I(self.lid..string.format("Added Radio Beacon %d Hertz | Name %s | Position {%d,%d,%d}",Frequency,BeaconName,vec3.x,vec3.y,vec3.z))
trigger.action.radioTransmission(Sound, vec3, 0, true, Frequency, self.ADFRadioPwr or 500,BeaconName) -- Beacon in MP only runs for exactly 30secs straight
end
end
@@ -2343,9 +2381,13 @@ function CSAR:_RefreshRadioBeacons()
local group = pilot.group
local frequency = pilot.frequency or 0 -- thanks to @Thrud
local bname = pilot.BeaconName or pilot.name..math.random(1,100000)
trigger.action.stopRadioTransmission(bname)
--trigger.action.stopRadioTransmission(bname)
if group and group:IsAlive() and frequency > 0 then
self:_AddBeaconToGroup(group,frequency,bname)
--self:_AddBeaconToGroup(group,frequency,bname)
else
if frequency > 0 then
trigger.action.stopRadioTransmission(bname)
end
end
end
end
@@ -2425,7 +2467,26 @@ function CSAR:onafterStart(From, Event, To)
self.allheligroupset = SET_GROUP:New():FilterCoalitions(self.coalitiontxt):FilterCategoryHelicopter():FilterStart()
end
self.mash = SET_GROUP:New():FilterCoalitions(self.coalitiontxt):FilterPrefixes(self.mashprefix):FilterStart() -- currently only GROUP objects, maybe support STATICs also?
self.mash = SET_GROUP:New():FilterCoalitions(self.coalitiontxt):FilterPrefixes(self.mashprefix):FilterStart()
self.staticmashes = SET_STATIC:New():FilterCoalitions(self.coalitiontxt):FilterPrefixes(self.mashprefix):FilterOnce()
self.zonemashes = SET_ZONE:New():FilterPrefixes(self.mashprefix):FilterOnce()
--[[
if staticmashes:Count() > 0 then
for _,_mash in pairs(staticmashes.Set) do
self.mash:AddObject(_mash)
end
end
if zonemashes:Count() > 0 then
self:T("Adding zones to self.mash SET")
for _,_mash in pairs(zonemashes.Set) do
self.mash:AddObject(_mash)
end
self:T("Objects in SET: "..self.mash:Count())
end
--]]
if not self.coordinate then
local csarhq = self.mash:GetRandom()

File diff suppressed because it is too large Load Diff

View File

@@ -88,7 +88,10 @@ COHORT = {
--- COHORT class version.
-- @field #string version
COHORT.version="0.3.5"
COHORT.version="0.3.6"
--- Global variable to store the unique(!) cohort names
_COHORTNAMES={}
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
@@ -110,6 +113,17 @@ COHORT.version="0.3.5"
-- @return #COHORT self
function COHORT:New(TemplateGroupName, Ngroups, CohortName)
-- Name of the cohort.
local name=tostring(CohortName or TemplateGroupName)
-- Cohort name has to be unique or we will get serious problems!
if UTILS.IsAnyInTable(_COHORTNAMES, name) then
env.error(string.format('ERROR: cannot create cohort "%s" because another cohort with that name already exists. Names must be unique!', name))
return nil
else
table.insert(_COHORTNAMES, name)
end
-- Inherit everything from FSM class.
local self=BASE:Inherit(self, FSM:New()) -- #COHORT
@@ -117,7 +131,7 @@ function COHORT:New(TemplateGroupName, Ngroups, CohortName)
self.templatename=TemplateGroupName
-- Cohort name.
self.name=tostring(CohortName or TemplateGroupName)
self.name=name
-- Set some string id for output to DCS.log file.
self.lid=string.format("COHORT %s | ", self.name)

View File

@@ -1774,8 +1774,10 @@ function COMMANDER:RecruitAssetsForMission(Mission)
MaxWeight=cohort.cargobayLimit
end
end
self:T(self.lid..string.format("Largest cargo bay available=%.1f", MaxWeight))
if MaxWeight then
self:T(self.lid..string.format("Largest cargo bay available=%.1f", MaxWeight))
end
end
local legions=self.legions
@@ -2165,4 +2167,4 @@ end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

View File

@@ -50,7 +50,7 @@
-- @field #number capleg
-- @field #number maxinterceptsize
-- @field #number missionrange
-- @field #number noaltert5
-- @field #number noalert5
-- @field #table ManagedAW
-- @field #table ManagedSQ
-- @field #table ManagedCP
@@ -62,6 +62,7 @@
-- @field #number repeatsonfailure
-- @field Core.Set#SET_ZONE GoZoneSet
-- @field Core.Set#SET_ZONE NoGoZoneSet
-- @field Core.Set#SET_ZONE ConflictZoneSet
-- @field #boolean Monitor
-- @field #boolean TankerInvisible
-- @field #number CapFormation
@@ -69,6 +70,7 @@
-- @field #boolean DespawnAfterLanding
-- @field #boolean DespawnAfterHolding
-- @field #list<Ops.Auftrag#AUFTRAG> ListOfAuftrag
-- @field #string defaulttakeofftype Take off type
-- @extends Core.Fsm#FSM
--- *“Airspeed, altitude, and brains. Two are always needed to successfully complete the flight.”* -- Unknown.
@@ -102,6 +104,11 @@
-- Next put a late activated template group for your CAP/GCI Squadron on the map. Last, put a zone on the map for the CAP operations, let's name it "Blue Zone 1". Size of the zone plays no role.
-- Put an EW radar system on the map and name it aptly, like "Blue EWR".
--
-- ### Zones
--
-- For our example, you create a RED and a BLUE border, as a closed polygonal zone representing the borderlines. You can also have conflict zone, where - for our example - BLUE will attack
-- RED planes, despite being on RED territory. Think of a no-fly zone or an limited area of engagement. Conflict zones take precedence over borders, i.e. they can overlap all borders.
--
-- ### Code it
--
-- -- Set up a basic system for the blue side, we'll reside on Kutaisi, and use GROUP objects with "Blue EWR" in the name as EW Radar Systems.
@@ -114,10 +121,10 @@
-- mywing:AddSquadron("Blue Sq1 M2000c","CAP Kutaisi",AIRBASE.Caucasus.Kutaisi,20,AI.Skill.GOOD,102,"ec1.5_Vendee_Jeanne_clean")
--
-- -- Add a couple of zones
-- -- We'll defend our border
-- -- We'll defend our own border
-- mywing:AddAcceptZone(ZONE_POLYGON:New( "Blue Border", GROUP:FindByName( "Blue Border" ) ))
-- -- We'll attack intruders also here
-- mywing:AddAcceptZone(ZONE_POLYGON:New("Red Defense Zone", GROUP:FindByName( "Red Defense Zone" )))
-- -- We'll attack intruders also here - conflictzones can overlap borders(!) - limited zone of engagement
-- mywing:AddConflictZone(ZONE_POLYGON:New("Red Defense Zone", GROUP:FindByName( "Red Defense Zone" )))
-- -- We'll leave the reds alone on their turf
-- mywing:AddRejectZone(ZONE_POLYGON:New( "Red Border", GROUP:FindByName( "Red Border" ) ))
--
@@ -125,10 +132,10 @@
-- -- Set up borders on map
-- local BlueBorder = ZONE_POLYGON:New( "Blue Border", GROUP:FindByName( "Blue Border" ) )
-- BlueBorder:DrawZone(-1,{0,0,1},1,FillColor,FillAlpha,1,true)
-- local BlueNoGoZone = ZONE_POLYGON:New("Red Defense Zone", GROUP:FindByName( "Red Defense Zone" ))
-- BlueNoGoZone:DrawZone(-1,{1,1,0},1,FillColor,FillAlpha,2,true)
-- local BlueNoGoZone2 = ZONE_POLYGON:New( "Red Border", GROUP:FindByName( "Red Border" ) )
-- BlueNoGoZone2:DrawZone(-1,{1,0,0},1,FillColor,FillAlpha,4,true)
-- local ConflictZone = ZONE_POLYGON:New("Red Defense Zone", GROUP:FindByName( "Red Defense Zone" ))
-- ConflictZone:DrawZone(-1,{1,1,0},1,FillColor,FillAlpha,2,true)
-- local BlueNoGoZone = ZONE_POLYGON:New( "Red Border", GROUP:FindByName( "Red Border" ) )
-- BlueNoGoZone:DrawZone(-1,{1,0,0},1,FillColor,FillAlpha,4,true)
--
-- ### Add a second airwing with squads and own CAP point (optional)
--
@@ -167,7 +174,7 @@
-- * @{#EASYGCICAP.SetDefaultCAPLeg}: Set the length of the CAP leg, default is 15 NM.
-- * @{#EASYGCICAP.SetDefaultCAPGrouping}: Set how many planes will be spawned per mission (CVAP/GCI), defaults to 2.
-- * @{#EASYGCICAP.SetDefaultMissionRange}: Set how many NM the planes can go from the home base, defaults to 100.
-- * @{#EASYGCICAP.SetDefaultNumberAlter5Standby}: Set how many planes will be spawned on cold standby (Alert5), default 2.
-- * @{#EASYGCICAP.SetDefaultNumberAlert5Standby}: Set how many planes will be spawned on cold standby (Alert5), default 2.
-- * @{#EASYGCICAP.SetDefaultEngageRange}: Set max engage range for CAP flights if they detect intruders, defaults to 50.
-- * @{#EASYGCICAP.SetMaxAliveMissions}: Set max parallel missions can be done (CAP+GCI+Alert5+Tanker+AWACS), defaults to 8.
-- * @{#EASYGCICAP.SetDefaultRepeatOnFailure}: Set max repeats on failure for intercepting/killing intruders, defaults to 3.
@@ -197,7 +204,7 @@ EASYGCICAP = {
capleg = 15,
maxinterceptsize = 2,
missionrange = 100,
noaltert5 = 4,
noalert5 = 4,
ManagedAW = {},
ManagedSQ = {},
ManagedCP = {},
@@ -210,13 +217,15 @@ EASYGCICAP = {
repeatsonfailure = 3,
GoZoneSet = nil,
NoGoZoneSet = nil,
ConflictZoneSet = nil,
Monitor = false,
TankerInvisible = true,
CapFormation = nil,
ReadyFlightGroups = {},
DespawnAfterLanding = false,
DespawnAfterHolding = true,
ListOfAuftrag = {}
ListOfAuftrag = {},
defaulttakeofftype = "hot",
}
--- Internal Squadron data type
@@ -252,7 +261,7 @@ EASYGCICAP = {
--- EASYGCICAP class version.
-- @field #string version
EASYGCICAP.version="0.1.13"
EASYGCICAP.version="0.1.21"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
@@ -269,7 +278,7 @@ EASYGCICAP.version="0.1.13"
-- @param #string Alias A Name for this GCICAP
-- @param #string AirbaseName Name of the Home Airbase
-- @param #string Coalition Coalition, e.g. "blue" or "red"
-- @param #string EWRName (Partial) group name of the EWR system of the coalition, e.g. "Red EWR"
-- @param #string EWRName (Partial) group name of the EWR system of the coalition, e.g. "Red EWR", can be handed in as table of names, e.g.{"EWR","Radar","SAM"}
-- @return #EASYGCICAP self
function EASYGCICAP:New(Alias, AirbaseName, Coalition, EWRName)
-- Inherit everything from FSM class.
@@ -278,14 +287,16 @@ function EASYGCICAP:New(Alias, AirbaseName, Coalition, EWRName)
-- defaults
self.alias = Alias or AirbaseName.." CAP Wing"
self.coalitionname = string.lower(Coalition) or "blue"
self.coalition = self.coaltitionname == "blue" and coalition.side.BLUE or coalition.side.RED
self.coalition = self.coalitionname == "blue" and coalition.side.BLUE or coalition.side.RED
self.wings = {}
self.EWRName = EWRName or self.coalitionname.." EWR"
if type(EWRName) == "string" then EWRName = {EWRName} end
self.EWRName = EWRName --or self.coalitionname.." EWR"
--self.CapZoneName = CapZoneName
self.airbasename = AirbaseName
self.airbase = AIRBASE:FindByName(self.airbasename)
self.GoZoneSet = SET_ZONE:New()
self.NoGoZoneSet = SET_ZONE:New()
self.ConflictZoneSet = SET_ZONE:New()
self.resurrection = 900
self.capspeed = 300
self.capalt = 25000
@@ -293,7 +304,7 @@ function EASYGCICAP:New(Alias, AirbaseName, Coalition, EWRName)
self.capleg = 15
self.capgrouping = 2
self.missionrange = 100
self.noaltert5 = 2
self.noalert5 = 2
self.MaxAliveMissions = 8
self.engagerange = 50
self.repeatsonfailure = 3
@@ -303,6 +314,7 @@ function EASYGCICAP:New(Alias, AirbaseName, Coalition, EWRName)
self.DespawnAfterLanding = false
self.DespawnAfterHolding = true
self.ListOfAuftrag = {}
self.defaulttakeofftype = "hot"
-- Set some string id for output to DCS.log file.
self.lid=string.format("EASYGCICAP %s | ", self.alias)
@@ -347,9 +359,23 @@ function EASYGCICAP:SetTankerAndAWACSInvisible(Switch)
return self
end
--- Set Maximum of alive missions to stop airplanes spamming the map
--- Count alive missions in our internal stack.
-- @param #EASYGCICAP self
-- @param #number Maxiumum Maxmimum number of parallel missions allowed. Count is Cap-Missions + Intercept-Missions + Alert5-Missionsm default is 6
-- @return #number count
function EASYGCICAP:_CountAliveAuftrags()
local alive = 0
for _,_auftrag in pairs(self.ListOfAuftrag) do
local auftrag = _auftrag -- Ops.Auftrag#AUFTRAG
if auftrag and (not (auftrag:IsCancelled() or auftrag:IsDone() or auftrag:IsOver())) then
alive = alive + 1
end
end
return alive
end
--- Set Maximum of alive missions created by this instance to stop airplanes spamming the map
-- @param #EASYGCICAP self
-- @param #number Maxiumum Maxmimum number of parallel missions allowed. Count is Intercept-Missions + Alert5-Missions, default is 8
-- @return #EASYGCICAP self
function EASYGCICAP:SetMaxAliveMissions(Maxiumum)
self:T(self.lid.."SetMaxAliveMissions")
@@ -377,6 +403,16 @@ function EASYGCICAP:SetDefaultRepeatOnFailure(Retries)
return self
end
--- Add default take off type for the airwings.
-- @param #EASYGCICAP self
-- @param #string Takeoff Can be "hot", "cold", or "air" - default is "hot".
-- @return #EASYGCICAP self
function EASYGCICAP:SetDefaultTakeOffType(Takeoff)
self:T(self.lid.."SetDefaultTakeOffType")
self.defaulttakeofftype = Takeoff or "hot"
return self
end
--- Set default CAP Speed in knots
-- @param #EASYGCICAP self
-- @param #number Speed Speed defaults to 300
@@ -441,9 +477,9 @@ end
-- @param #EASYGCICAP self
-- @param #number Airframes defaults to 2
-- @return #EASYGCICAP self
function EASYGCICAP:SetDefaultNumberAlter5Standby(Airframes)
self:T(self.lid.."SetDefaultNumberAlter5Standby")
self.noaltert5 = math.abs(Airframes) or 2
function EASYGCICAP:SetDefaultNumberAlert5Standby(Airframes)
self:T(self.lid.."SetDefaultNumberAlert5Standby")
self.noalert5 = math.abs(Airframes) or 2
return self
end
@@ -452,7 +488,7 @@ end
-- @param #number Range defaults to 50NM
-- @return #EASYGCICAP self
function EASYGCICAP:SetDefaultEngageRange(Range)
self:T(self.lid.."SetDefaultNumberAlter5Standby")
self:T(self.lid.."SetDefaultEngageRange")
self.engagerange = Range or 50
return self
end
@@ -573,9 +609,8 @@ function EASYGCICAP:_AddAirwing(Airbasename, Alias)
if #self.ManagedREC > 0 then
CAP_Wing:SetNumberRecon(1)
end
--local PatrolCoordinateKutaisi = ZONE:New(CapZoneName):GetCoordinate()
--CAP_Wing:AddPatrolPointCAP(PatrolCoordinateKutaisi,self.capalt,UTILS.KnotsToAltKIAS(self.capspeed,self.capalt),self.capdir,self.capleg)
CAP_Wing:SetTakeoffHot()
CAP_Wing:SetTakeoffType(self.defaulttakeofftype)
CAP_Wing:SetLowFuelThreshold(0.3)
CAP_Wing.RandomAssetScore = math.random(50,100)
CAP_Wing:Start()
@@ -583,8 +618,11 @@ function EASYGCICAP:_AddAirwing(Airbasename, Alias)
local Intel = self.Intel
local TankerInvisible = self.TankerInvisible
local engagerange = self.engagerange
local GoZoneSet = self.GoZoneSet
local NoGoZoneSet = self.NoGoZoneSet
function CAP_Wing:OnAfterFlightOnMission(From, Event, To, Flightgroup, Mission)
function CAP_Wing:onbeforeFlightOnMission(From, Event, To, Flightgroup, Mission)
local flightgroup = Flightgroup -- Ops.FlightGroup#FLIGHTGROUP
if DespawnAfterLanding then
flightgroup:SetDespawnAfterLanding()
@@ -596,7 +634,7 @@ function EASYGCICAP:_AddAirwing(Airbasename, Alias)
flightgroup:GetGroup():SetOptionRadarUsingForContinousSearch()
if Mission.type ~= AUFTRAG.Type.TANKER and Mission.type ~= AUFTRAG.Type.AWACS and Mission.type ~= AUFTRAG.Type.RECON then
flightgroup:SetDetection(true)
flightgroup:SetEngageDetectedOn(self.engagerange,{"Air"},self.GoZoneSet,self.NoGoZoneSet)
flightgroup:SetEngageDetectedOn(engagerange,{"Air"},GoZoneSet,NoGoZoneSet)
flightgroup:SetOutOfAAMRTB()
if CapFormation then
flightgroup:GetGroup():SetOption(AI.Option.Air.id.FORMATION,CapFormation)
@@ -614,15 +652,15 @@ function EASYGCICAP:_AddAirwing(Airbasename, Alias)
flightgroup:SetFuelLowRTB(true)
Intel:AddAgent(flightgroup)
if DespawnAfterHolding then
function flightgroup:OnAfterHolding(From,Event,To)
function flightgroup:onbeforeHolding(From,Event,To)
self:Despawn(1,true)
end
end
end
if self.noaltert5 > 0 then
if self.noalert5 > 0 then
local alert = AUFTRAG:NewALERT5(AUFTRAG.Type.INTERCEPT)
alert:SetRequiredAssets(self.noaltert5)
alert:SetRequiredAssets(self.noalert5)
alert:SetRepeat(99)
CAP_Wing:AddMission(alert)
table.insert(self.ListOfAuftrag,alert)
@@ -1098,7 +1136,7 @@ end
-- @param Core.Zone#ZONE_BASE Zone
-- @return #EASYGCICAP self
function EASYGCICAP:AddAcceptZone(Zone)
self:T(self.lid.."AddAcceptZone0")
self:T(self.lid.."AddAcceptZone")
self.GoZoneSet:AddZone(Zone)
return self
end
@@ -1113,6 +1151,18 @@ function EASYGCICAP:AddRejectZone(Zone)
return self
end
--- Add a zone to the conflict zones set.
-- @param #EASYGCICAP self
-- @param Core.Zone#ZONE_BASE Zone
-- @return #EASYGCICAP self
function EASYGCICAP:AddConflictZone(Zone)
self:T(self.lid.."AddConflictZone")
self.ConflictZoneSet:AddZone(Zone)
self.GoZoneSet:AddZone(Zone)
return self
end
--- (Internal) Try to assign the intercept to a FlightGroup already in air and ready.
-- @param #EASYGCICAP self
-- @param #table ReadyFlightGroups ReadyFlightGroups
@@ -1176,8 +1226,9 @@ function EASYGCICAP:_AssignIntercept(Cluster)
local wings = self.wings
local ctlpts = self.ManagedCP
local MaxAliveMissions = self.MaxAliveMissions * self.capgrouping
local MaxAliveMissions = self.MaxAliveMissions --* self.capgrouping
local nogozoneset = self.NoGoZoneSet
local conflictzoneset = self.ConflictZoneSet
local ReadyFlightGroups = self.ReadyFlightGroups
-- Aircraft?
@@ -1241,9 +1292,10 @@ function EASYGCICAP:_AssignIntercept(Cluster)
-- Do we have a matching airwing?
if targetairwing then
local AssetCount = targetairwing:CountAssetsOnMission(MissionTypes,Cohort)
local missioncount = self:_CountAliveAuftrags()
-- Enough airframes on mission already?
self:T(self.lid.." Assets on Mission "..AssetCount)
if AssetCount <= MaxAliveMissions then
if missioncount < MaxAliveMissions then
local repeats = repeatsonfailure
local InterceptAuftrag = AUFTRAG:NewINTERCEPT(contact.group)
:SetMissionRange(150)
@@ -1255,18 +1307,22 @@ function EASYGCICAP:_AssignIntercept(Cluster)
if nogozoneset:Count() > 0 then
InterceptAuftrag:AddConditionSuccess(
function(group,zoneset)
function(group,zoneset,conflictset)
local success = false
if group and group:IsAlive() then
local coord = group:GetCoordinate()
if coord and zoneset:IsCoordinateInZone(coord) then
if coord and zoneset:Count() > 0 and zoneset:IsCoordinateInZone(coord) then
success = true
end
if coord and conflictset:Count() > 0 and conflictset:IsCoordinateInZone(coord) then
success = false
end
end
return success
end,
contact.group,
nogozoneset
nogozoneset,
conflictzoneset
)
end
@@ -1291,15 +1347,16 @@ function EASYGCICAP:_StartIntel()
self:T(self.lid.."_StartIntel")
-- Border GCI Detection
local BlueAir_DetectionSetGroup = SET_GROUP:New()
BlueAir_DetectionSetGroup:FilterPrefixes( { self.EWRName } )
BlueAir_DetectionSetGroup:FilterPrefixes( self.EWRName )
BlueAir_DetectionSetGroup:FilterStart()
-- Intel type detection
local BlueIntel = INTEL:New(BlueAir_DetectionSetGroup,self.coalitionname, self.EWRName)
local BlueIntel = INTEL:New(BlueAir_DetectionSetGroup,self.coalitionname, self.alias)
BlueIntel:SetClusterAnalysis(true,false,false)
BlueIntel:SetForgetTime(300)
BlueIntel:SetAcceptZones(self.GoZoneSet)
BlueIntel:SetRejectZones(self.NoGoZoneSet)
BlueIntel:SetConflictZones(self.ConflictZoneSet)
BlueIntel:SetVerbosity(0)
BlueIntel:Start()
@@ -1311,7 +1368,7 @@ function EASYGCICAP:_StartIntel()
self:_AssignIntercept(Cluster)
end
function BlueIntel:OnAfterNewCluster(From,Event,To,Cluster)
function BlueIntel:onbeforeNewCluster(From,Event,To,Cluster)
AssignCluster(Cluster)
end
@@ -1428,12 +1485,14 @@ function EASYGCICAP:onafterStatus(From,Event,To)
local text = "GCICAP "..self.alias
text = text.."\nWings: "..wings.."\nSquads: "..squads.."\nCapPoints: "..caps.."\nAssets on Mission: "..assets.."\nAssets in Stock: "..instock
text = text.."\nThreats: "..threatcount
text = text.."\nMissions: "..capmission+interceptmission
text = text.."\nAirWing managed Missions: "..capmission+awacsmission+tankermission+reconmission
text = text.."\n - CAP: "..capmission
text = text.."\n - Intercept: "..interceptmission
text = text.."\n - AWACS: "..awacsmission
text = text.."\n - TANKER: "..tankermission
text = text.."\n - Recon: "..reconmission
text = text.."\nSelf managed Missions:"
text = text.."\n - Mission Limit: "..self.MaxAliveMissions
text = text.."\n - Alert5+Intercept "..self:_CountAliveAuftrags()
MESSAGE:New(text,15,"GCICAP"):ToAll():ToLogIf(self.debug)
end
self:__Status(30)

View File

@@ -217,7 +217,7 @@ FLIGHTGROUP.Players={}
--- FLIGHTGROUP class version.
-- @field #string version
FLIGHTGROUP.version="1.0.2"
FLIGHTGROUP.version="1.0.3"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
@@ -259,7 +259,7 @@ function FLIGHTGROUP:New(group)
local self=BASE:Inherit(self, OPSGROUP:New(group)) -- #FLIGHTGROUP
-- Set some string id for output to DCS.log file.
self.lid=string.format("FLIGHTGROUP %s | ", self.groupname)
self.lid=string.format("FLIGHTGROUP %s | ", self.groupname or "N/A")
-- Defaults
self:SetDefaultROE()
@@ -1277,6 +1277,25 @@ function FLIGHTGROUP:Status()
end
end
--- check if we need to end holding
--self:T(self.lid.."Checking if we are holding at a holding point...")
if mission and mission.missionHoldingCoord and self.isHoldingAtHoldingPoint == true then
self:T(self.lid.."...yes")
if mission:IsReadyToPush() then
--self:T(self.lid.."Ready to push -> YES")
-- move flag to 1
self.flaghold:Set(1)
-- Not waiting any more.
self.Twaiting=nil
self.dTwait=nil
self.isHoldingAtHoldingPoint = false
--else
--self:T(self.lid.."Ready to push -> NO!")
end
--else
--self:T(self.lid.."...no")
end
-- If mission, check if DCS task needs to be updated.
if mission and mission.updateDCSTask then
@@ -3803,114 +3822,124 @@ end
--- Initialize group parameters. Also initializes waypoints if self.waypoints is nil.
-- @param #FLIGHTGROUP self
-- @param #table Template Template used to init the group. Default is `self.template`.
-- @param #number Delay Delay in seconds before group is initialized. Default `nil`, *i.e.* instantaneous.
-- @return #FLIGHTGROUP self
function FLIGHTGROUP:_InitGroup(Template)
function FLIGHTGROUP:_InitGroup(Template, Delay)
-- First check if group was already initialized.
if self.groupinitialized then
self:T(self.lid.."WARNING: Group was already initialized! Will NOT do it again!")
return
end
-- Group object.
local group=self.group --Wrapper.Group#GROUP
-- Helo group.
self.isHelo=group:IsHelicopter()
-- Max speed in km/h.
self.speedMax=group:GetSpeedMax()
-- Is group mobile?
if self.speedMax and self.speedMax>3.6 then
self.isMobile=true
if Delay and Delay>0 then
self:ScheduleOnce(Delay, FLIGHTGROUP._InitGroup, self, Template, 0)
else
self.isMobile=false
self.speedMax = 0
end
-- Cruise speed limit 380 kts for fixed and 110 knots for rotary wings.
local speedCruiseLimit=self.isHelo and UTILS.KnotsToKmph(110) or UTILS.KnotsToKmph(380)
-- Cruise speed: 70% of max speed but within limit.
self.speedCruise=math.min(self.speedMax*0.7, speedCruiseLimit)
-- Group ammo.
self.ammo=self:GetAmmoTot()
-- Get template of group.
local template=Template or self:_GetTemplate()
-- Is (template) group uncontrolled.
self.isUncontrolled=template~=nil and template.uncontrolled or false
-- Is (template) group late activated.
self.isLateActivated=template~=nil and template.lateActivation or false
if template then
-- Radio parameters from template. Default is set on spawn if not modified by user.
self.radio.Freq=tonumber(template.frequency)
self.radio.Modu=tonumber(template.modulation)
self.radio.On=template.communication
-- Set callsign. Default is set on spawn if not modified by user.
local callsign=template.units[1].callsign
--self:I({callsign=callsign})
if type(callsign)=="number" then -- Sometimes callsign is just "101".
local cs=tostring(callsign)
callsign={}
callsign[1]=cs:sub(1,1)
callsign[2]=cs:sub(2,2)
callsign[3]=cs:sub(3,3)
-- First check if group was already initialized.
if self.groupinitialized then
self:T(self.lid.."WARNING: Group was already initialized! Will NOT do it again!")
return
end
self.callsign.NumberSquad=tonumber(callsign[1])
self.callsign.NumberGroup=tonumber(callsign[2])
self.callsign.NameSquad=UTILS.GetCallsignName(self.callsign.NumberSquad)
-- Group object.
local group=self.group --Wrapper.Group#GROUP
-- Helo group.
self.isHelo=group:IsHelicopter()
-- Max speed in km/h.
self.speedMax=group:GetSpeedMax()
end
-- Set default formation.
if self.isHelo then
self.optionDefault.Formation=ENUMS.Formation.RotaryWing.EchelonLeft.D300
else
self.optionDefault.Formation=ENUMS.Formation.FixedWing.EchelonLeft.Group
end
-- Default TACAN off.
self:SetDefaultTACAN(nil, nil, nil, nil, true)
self.tacan=UTILS.DeepCopy(self.tacanDefault)
-- Is this purely AI?
self.isAI=not self:_IsHuman(group)
-- Create Menu.
if not self.isAI then
self.menu=self.menu or {}
self.menu.atc=self.menu.atc or {} --#table
self.menu.atc.root=self.menu.atc.root or MENU_GROUP:New(self.group, "ATC") --Core.Menu#MENU_GROUP
self.menu.atc.help=self.menu.atc.help or MENU_GROUP:New(self.group, "Help", self.menu.atc.root) --Core.Menu#MENU_GROUP
end
-- Units of the group.
local units=self.group:GetUnits()
-- Is group mobile?
if self.speedMax and self.speedMax>3.6 then
self.isMobile=true
else
self.isMobile=false
self.speedMax = 0
end
-- DCS group.
local dcsgroup=Group.getByName(self.groupname)
local size0=dcsgroup:getInitialSize()
-- Cruise speed limit 380 kts for fixed and 110 knots for rotary wings.
local speedCruiseLimit=self.isHelo and UTILS.KnotsToKmph(110) or UTILS.KnotsToKmph(380)
-- Quick check.
if #units~=size0 then
self:T(self.lid..string.format("ERROR: Got #units=%d but group consists of %d units!", #units, size0))
end
-- Add elemets.
for _,unit in pairs(units) do
self:_AddElementByName(unit:GetName())
-- Cruise speed: 70% of max speed but within limit.
self.speedCruise=math.min(self.speedMax*0.7, speedCruiseLimit)
-- Group ammo.
self.ammo=self:GetAmmoTot()
-- Get template of group.
local template=Template or self:_GetTemplate()
-- Is (template) group uncontrolled.
self.isUncontrolled=template~=nil and template.uncontrolled or false
-- Is (template) group late activated.
self.isLateActivated=template~=nil and template.lateActivation or false
if template then
-- Radio parameters from template. Default is set on spawn if not modified by user.
self.radio.Freq=tonumber(template.frequency)
self.radio.Modu=tonumber(template.modulation)
self.radio.On=template.communication
-- Set callsign. Default is set on spawn if not modified by user.
local callsign=template.units[1].callsign
--self:I({callsign=callsign})
if type(callsign)=="number" then -- Sometimes callsign is just "101".
local cs=tostring(callsign)
callsign={}
callsign[1]=cs:sub(1,1)
callsign[2]=cs:sub(2,2)
callsign[3]=cs:sub(3,3)
end
self.callsign.NumberSquad=tonumber(callsign[1])
self.callsign.NumberGroup=tonumber(callsign[2])
self.callsign.NameSquad=UTILS.GetCallsignName(self.callsign.NumberSquad)
end
-- Set default formation.
if self.isHelo then
self.optionDefault.Formation=ENUMS.Formation.RotaryWing.EchelonLeft.D300
else
self.optionDefault.Formation=ENUMS.Formation.FixedWing.EchelonLeft.Group
end
-- Default TACAN off.
if not self.tacanDefault then
self:SetDefaultTACAN(nil, nil, nil, nil, true)
end
if not self.tacan then
self.tacan=UTILS.DeepCopy(self.tacanDefault)
end
-- Is this purely AI?
self.isAI=not self:_IsHuman(group)
-- Create Menu.
if not self.isAI then
self.menu=self.menu or {}
self.menu.atc=self.menu.atc or {} --#table
self.menu.atc.root=self.menu.atc.root or MENU_GROUP:New(self.group, "ATC") --Core.Menu#MENU_GROUP
self.menu.atc.help=self.menu.atc.help or MENU_GROUP:New(self.group, "Help", self.menu.atc.root) --Core.Menu#MENU_GROUP
end
-- Units of the group.
local units=self.group:GetUnits()
-- DCS group.
local dcsgroup=Group.getByName(self.groupname)
local size0=dcsgroup:getInitialSize()
-- Quick check.
if #units~=size0 then
self:T(self.lid..string.format("ERROR: Got #units=%d but group consists of %d units!", #units, size0))
end
-- Add elemets.
for _,unit in pairs(units) do
self:_AddElementByName(unit:GetName())
end
-- Init done.
self.groupinitialized=true
end
-- Init done.
self.groupinitialized=true
return self
end
@@ -4179,51 +4208,6 @@ function FLIGHTGROUP:IsLandingAirbase(wp)
return nil
end
--- Initialize Mission Editor waypoints.
-- @param #FLIGHTGROUP self
-- @return #FLIGHTGROUP self
function FLIGHTGROUP:InitWaypoints()
-- Template waypoints.
self.waypoints0=self.group:GetTemplateRoutePoints()
-- Waypoints
self.waypoints={}
for index,wp in pairs(self.waypoints0) do
local waypoint=self:_CreateWaypoint(wp)
self:_AddWaypoint(waypoint)
end
-- Get home and destination airbases from waypoints.
self.homebase=self.homebase or self:GetHomebaseFromWaypoints()
self.destbase=self.destbase or self:GetDestinationFromWaypoints()
self.currbase=self:GetHomebaseFromWaypoints()
-- Remove the landing waypoint. We use RTB for that. It makes adding new waypoints easier as we do not have to check if the last waypoint is the landing waypoint.
if self.destbase and #self.waypoints>1 then
table.remove(self.waypoints, #self.waypoints)
else
self.destbase=self.homebase
end
-- Debug info.
self:T(self.lid..string.format("Initializing %d waypoints. Homebase %s ==> %s Destination", #self.waypoints, self.homebase and self.homebase:GetName() or "unknown", self.destbase and self.destbase:GetName() or "uknown"))
-- Update route.
if #self.waypoints>0 then
-- Check if only 1 wp?
if #self.waypoints==1 then
self:_PassedFinalWaypoint(true, "FLIGHTGROUP:InitWaypoints #self.waypoints==1")
end
end
return self
end
--- Add an AIR waypoint to the flight plan.
-- @param #FLIGHTGROUP self

View File

@@ -2324,7 +2324,7 @@ INTEL_DLINK = {
verbose = 0,
lid = nil,
alias = nil,
cachetime = 300,
cachetime = 120,
interval = 20,
contacts = {},
clusters = {},
@@ -2333,7 +2333,7 @@ INTEL_DLINK = {
--- Version string
-- @field #string version
INTEL_DLINK.version = "0.0.1"
INTEL_DLINK.version = "0.0.2"
--- Function to instantiate a new object
-- @param #INTEL_DLINK self
@@ -2384,15 +2384,15 @@ function INTEL_DLINK:New(Intels, Alias, Interval, Cachetime)
self.alias="SPECTRE"
end
-- Cache time
self.cachetime = Cachetime or 300
-- Interval
self.interval = Interval or 20
-- Set some string id for output to DCS.log file.
self.lid=string.format("INTEL_DLINK %s | ", self.alias)
-- Cache time
self:SetDLinkCacheTime(Cachetime or 120)
-- Start State.
self:SetStartState("Stopped")
@@ -2477,6 +2477,16 @@ function INTEL_DLINK:onafterStart(From, Event, To)
return self
end
--- Function to set how long INTEL DLINK remembers contacts.
-- @param #INTEL_DLINK self
-- @param #number seconds Remember this many seconds. Defaults to 180.
-- @return #INTEL_DLINK self
function INTEL_DLINK:SetDLinkCacheTime(seconds)
self.cachetime = math.abs(seconds or 120)
self:I(self.lid.."Caching for "..self.cachetime.." seconds.")
return self
end
--- Function to collect data from the various #INTEL
-- @param #INTEL_DLINK self
-- @param #string From The From state

View File

@@ -53,7 +53,7 @@ LEGION.RandomAssetScore=1
--- LEGION class version.
-- @field #string version
LEGION.version="0.5.0"
LEGION.version="0.5.1"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ToDo list
@@ -445,6 +445,21 @@ function LEGION:DelCohort(Cohort)
return self
end
--- Remove specific asset from legion.
-- @param #LEGION self
-- @param Functional.Warehouse#WAREHOUSE.Assetitem Asset The asset.
-- @return #LEGION self
function LEGION:DelAsset(Asset)
if Asset.cohort then
Asset.cohort:DelAsset(Asset)
else
self:E(self.lid..string.format("ERROR: Asset has not cohort attached. Cannot remove it from legion!"))
end
return self
end
--- Relocate a cohort to another legion.
-- Assets in stock are spawned and routed to the new legion.
@@ -1643,6 +1658,9 @@ function LEGION:onafterAssetDead(From, Event, To, asset, request)
if self.commander and self.commander.chief then
self.commander.chief.detectionset:RemoveGroupsByName({asset.spawngroupname})
end
-- Remove asset from cohort and legion.
self:DelAsset(asset)
-- Remove asset from mission is done via Mission:AssetDead() call from flightgroup onafterFlightDead function
-- Remove asset from squadron same
@@ -1827,6 +1845,9 @@ function LEGION:_CreateFlightGroup(asset)
-- Set home base.
opsgroup.homebase=self.airbase
-- Set destination base
opsgroup.destbase=self.airbase
-- Set home zone.
opsgroup.homezone=self.spawnzone

View File

@@ -91,7 +91,7 @@ NAVYGROUP = {
--- NavyGroup version.
-- @field #string version
NAVYGROUP.version="1.0.2"
NAVYGROUP.version="1.0.3"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
@@ -395,7 +395,7 @@ function NAVYGROUP:New(group)
self:HandleEvent(EVENTS.Birth, self.OnEventBirth)
self:HandleEvent(EVENTS.Dead, self.OnEventDead)
self:HandleEvent(EVENTS.RemoveUnit, self.OnEventRemoveUnit)
self:HandleEvent(EVENTS.UnitLost, self.OnEventRemoveUnit)
self:HandleEvent(EVENTS.UnitLost, self.OnEventRemoveUnit)
-- Start the status monitoring.
self.timerStatus=TIMER:New(self.Status, self):Start(1, 30)
@@ -775,7 +775,7 @@ end
--- Update status.
-- @param #NAVYGROUP self
function NAVYGROUP:Status(From, Event, To)
function NAVYGROUP:Status()
-- FSM state.
local fsmstate=self:GetState()
@@ -978,6 +978,35 @@ function NAVYGROUP:Status(From, Event, To)
end
---
-- Elements
---
if self.verbose>=2 then
local text="Elements:"
for i,_element in pairs(self.elements) do
local element=_element --Ops.OpsGroup#OPSGROUP.Element
local name=element.name
local status=element.status
local unit=element.unit
local life,life0=self:GetLifePoints(element)
local life0=element.life0
-- Get ammo.
local ammo=self:GetAmmoElement(element)
-- Output text for element.
text=text..string.format("\n[%d] %s: status=%s, life=%.1f/%.1f, guns=%d, rockets=%d, bombs=%d, missiles=%d, cargo=%d/%d kg",
i, name, status, life, life0, ammo.Guns, ammo.Rockets, ammo.Bombs, ammo.Missiles, element.weightCargo, element.weightMaxCargo)
end
if #self.elements==0 then
text=text.." none!"
end
self:I(self.lid..text)
end
---
-- Engage Detected Targets
---
@@ -1041,7 +1070,7 @@ function NAVYGROUP:onafterSpawned(From, Event, To)
-- Debug info.
if self.verbose>=1 then
local text=string.format("Initialized Navy Group %s:\n", self.groupname)
local text=string.format("Initialized Navy Group %s [GID=%d]:\n", self.groupname, self.group:GetID())
text=text..string.format("Unit type = %s\n", self.actype)
text=text..string.format("Speed max = %.1f Knots\n", UTILS.KmphToKnots(self.speedMax))
text=text..string.format("Speed cruise = %.1f Knots\n", UTILS.KmphToKnots(self.speedCruise))
@@ -1269,7 +1298,7 @@ function NAVYGROUP:onafterUpdateRoute(From, Event, To, n, N, Speed, Depth)
if self.verbose>=10 then
for i=1,#waypoints do
local wp=waypoints[i] --Ops.OpsGroup#OPSGROUP.Waypoint
local text=string.format("%s Waypoint [%d] UID=%d speed=%d", self.groupname, i-1, wp.uid or -1, wp.speed)
local text=string.format("%s Waypoint [%d] UID=%d speed=%d m/s", self.groupname, i-1, wp.uid or -1, wp.speed)
self:I(self.lid..text)
COORDINATE:NewFromWaypoint(wp):MarkToAll(text)
end
@@ -1841,81 +1870,96 @@ end
--- Initialize group parameters. Also initializes waypoints if self.waypoints is nil.
-- @param #NAVYGROUP self
-- @param #table Template Template used to init the group. Default is `self.template`.
-- @param #number Delay Delay in seconds before group is initialized. Default `nil`, *i.e.* instantaneous.
-- @return #NAVYGROUP self
function NAVYGROUP:_InitGroup(Template)
function NAVYGROUP:_InitGroup(Template, Delay)
-- First check if group was already initialized.
if self.groupinitialized then
self:T(self.lid.."WARNING: Group was already initialized! Will NOT do it again!")
return
end
-- Get template of group.
local template=Template or self:_GetTemplate()
-- Ships are always AI.
self.isAI=true
-- Is (template) group late activated.
self.isLateActivated=template.lateActivation
-- Naval groups cannot be uncontrolled.
self.isUncontrolled=false
-- Max speed in km/h.
self.speedMax=self.group:GetSpeedMax()
-- Is group mobile?
if self.speedMax and self.speedMax>3.6 then
self.isMobile=true
if Delay and Delay>0 then
-- Delayed call
self:ScheduleOnce(Delay, NAVYGROUP._InitGroup, self, Template, 0)
else
self.isMobile=false
self.speedMax = 0
end
-- Cruise speed: 70% of max speed.
self.speedCruise=self.speedMax*0.7
-- First check if group was already initialized.
if self.groupinitialized then
self:T(self.lid.."WARNING: Group was already initialized! Will NOT do it again!")
return
end
-- Group ammo.
self.ammo=self:GetAmmoTot()
-- Get template of group.
local template=Template or self:_GetTemplate()
-- Radio parameters from template. Default is set on spawn if not modified by the user.
self.radio.On=true -- Radio is always on for ships.
self.radio.Freq=tonumber(template.units[1].frequency)/1000000
self.radio.Modu=tonumber(template.units[1].modulation)
-- Ships are always AI.
self.isAI=true
-- Is (template) group late activated.
self.isLateActivated=template.lateActivation
-- Naval groups cannot be uncontrolled.
self.isUncontrolled=false
-- Max speed in km/h.
self.speedMax=self.group:GetSpeedMax()
-- Is group mobile?
if self.speedMax and self.speedMax>3.6 then
self.isMobile=true
else
self.isMobile=false
self.speedMax = 0
end
-- Cruise speed: 70% of max speed.
self.speedCruise=self.speedMax*0.7
-- Group ammo.
self.ammo=self:GetAmmoTot()
-- Radio parameters from template. Default is set on spawn if not modified by the user.
self.radio.On=true -- Radio is always on for ships.
self.radio.Freq=tonumber(template.units[1].frequency)/1000000
self.radio.Modu=tonumber(template.units[1].modulation)
-- Set default formation. No really applicable for ships.
self.optionDefault.Formation="Off Road"
self.option.Formation=self.optionDefault.Formation
-- Set default formation. No really applicable for ships.
self.optionDefault.Formation="Off Road"
self.option.Formation=self.optionDefault.Formation
-- Default TACAN off.
self:SetDefaultTACAN(nil, nil, nil, nil, true)
self.tacan=UTILS.DeepCopy(self.tacanDefault)
-- Default TACAN off (we check if something is set already to keep those values in case of respawn)
if not self.tacanDefault then
self:SetDefaultTACAN(nil, nil, nil, nil, true)
end
if not self.tacan then
self.tacan=UTILS.DeepCopy(self.tacanDefault)
end
-- Default ICLS off.
if not self.iclsDefault then
self:SetDefaultICLS(nil, nil, nil, true)
end
if not self.icls then
self.icls=UTILS.DeepCopy(self.iclsDefault)
end
-- Get all units of the group.
local units=self.group:GetUnits()
-- Default ICLS off.
self:SetDefaultICLS(nil, nil, nil, true)
self.icls=UTILS.DeepCopy(self.iclsDefault)
-- Get all units of the group.
local units=self.group:GetUnits()
-- DCS group.
local dcsgroup=Group.getByName(self.groupname)
local size0=dcsgroup:getInitialSize()
-- Quick check.
if #units~=size0 then
self:E(self.lid..string.format("ERROR: Got #units=%d but group consists of %d units!", #units, size0))
-- DCS group.
local dcsgroup=Group.getByName(self.groupname)
local size0=dcsgroup:getInitialSize()
-- Quick check.
if #units~=size0 then
self:E(self.lid..string.format("ERROR: Got #units=%d but group consists of %d units!", #units, size0))
end
-- Add elemets.
for _,unit in pairs(units) do
self:_AddElementByName(unit:GetName())
end
-- Init done.
self.groupinitialized=true
end
-- Add elemets.
for _,unit in pairs(units) do
self:_AddElementByName(unit:GetName())
end
-- Init done.
self.groupinitialized=true
return self
end

View File

@@ -512,7 +512,7 @@ OPSGROUP.CargoStatus={
--- OpsGroup version.
-- @field #string version
OPSGROUP.version="1.0.1"
OPSGROUP.version="1.0.4"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
@@ -1041,7 +1041,7 @@ function OPSGROUP:SetReturnToLegion(Switch)
else
self.legionReturn=true
end
self:T(self.lid..string.format("Setting ReturnToLetion=%s", tostring(self.legionReturn)))
self:T(self.lid..string.format("Setting ReturnToLegion=%s", tostring(self.legionReturn)))
return self
end
@@ -1333,8 +1333,9 @@ end
-- @param Core.Point#COORDINATE TargetCoord Coordinate of the target.
-- @param #number WeaponBitType Weapon type.
-- @param Core.Point#COORDINATE RefCoord Reference coordinate.
-- @param #table SurfaceTypes Valid surfaces types of the coordinate. Default any (nil).
-- @return Core.Point#COORDINATE Coordinate in weapon range
function OPSGROUP:GetCoordinateInRange(TargetCoord, WeaponBitType, RefCoord)
function OPSGROUP:GetCoordinateInRange(TargetCoord, WeaponBitType, RefCoord, SurfaceTypes)
local coordInRange=nil --Core.Point#COORDINATE
@@ -1343,35 +1344,58 @@ function OPSGROUP:GetCoordinateInRange(TargetCoord, WeaponBitType, RefCoord)
-- Get weapon range.
local weapondata=self:GetWeaponData(WeaponBitType)
-- Heading intervals to search for a possible new coordinate in range.
local dh={0, -5, 5, -10, 10, -15, 15, -20, 20, -25, 25, -30, 30, -35, 35, -40, 40, -45, 45, -50, 50, -55, 55, -60, 60, -65, 65, -70, 70, -75, 75, -80, 80}
-- Function that checks if the given surface type is valid
local function _checkSurface(point)
if SurfaceTypes then
local stype=point:GetSurfaceType()
for _,sf in pairs(SurfaceTypes) do
if sf==stype then
return true
end
end
return false
else
return true
end
end
if weapondata then
-- Heading to target.
local heading=RefCoord:HeadingTo(TargetCoord)
local heading=TargetCoord:HeadingTo(RefCoord)
-- Distance to target.
local dist=RefCoord:Get2DDistance(TargetCoord)
local range=nil
if dist>weapondata.RangeMax then
range=weapondata.RangeMax
self:T(self.lid..string.format("Out of max range = %.1f km by %.1f km for weapon %s", weapondata.RangeMax/1000, (weapondata.RangeMax-dist)/1000, tostring(WeaponBitType)))
elseif dist<weapondata.RangeMin then
range=weapondata.RangeMin
self:T(self.lid..string.format("Out of min range = %.1f km by %.1f km for weapon %s", weapondata.RangeMin/1000, (weapondata.RangeMin-dist)/1000, tostring(WeaponBitType)))
end
-- Check if we are within range.
if dist>weapondata.RangeMax then
local d=(dist-weapondata.RangeMax)*1.05
-- New waypoint coord.
coordInRange=RefCoord:Translate(d, heading)
-- Debug info.
self:T(self.lid..string.format("Out of max range = %.1f km for weapon %s", weapondata.RangeMax/1000, tostring(WeaponBitType)))
elseif dist<weapondata.RangeMin then
local d=(dist-weapondata.RangeMin)*1.05
-- New waypoint coord.
coordInRange=RefCoord:Translate(d, heading)
-- Debug info.
self:T(self.lid..string.format("Out of min range = %.1f km for weapon %s", weapondata.RangeMax/1000, tostring(WeaponBitType)))
else
if range then
for _,delta in pairs(dh) do
local h=heading+delta
-- New waypoint coord.
coordInRange=TargetCoord:Translate(range, h)
if _checkSurface(coordInRange) then
break
end
end
else
-- Debug info.
self:T(self.lid..string.format("Already in range for weapon %s", tostring(WeaponBitType)))
end
@@ -1450,11 +1474,14 @@ end
-- @param #number RangeMin Minimum range in nautical miles. Default 0 NM.
-- @param #number RangeMax Maximum range in nautical miles. Default 10 NM.
-- @param #number BitType Bit mask of weapon type for which the given min/max ranges apply. Default is `ENUMS.WeaponFlag.Auto`, i.e. for all weapon types.
-- @param #function ConversionToMeters Function that converts input units of ranges to meters. Defaul `UTILS.NMToMeters`.
-- @return #OPSGROUP self
function OPSGROUP:AddWeaponRange(RangeMin, RangeMax, BitType)
function OPSGROUP:AddWeaponRange(RangeMin, RangeMax, BitType, ConversionToMeters)
RangeMin=UTILS.NMToMeters(RangeMin or 0)
RangeMax=UTILS.NMToMeters(RangeMax or 10)
ConversionToMeters=ConversionToMeters or UTILS.NMToMeters
RangeMin=ConversionToMeters(RangeMin or 0)
RangeMax=ConversionToMeters(RangeMax or 10)
local weapon={} --#OPSGROUP.WeaponData
@@ -4169,7 +4196,7 @@ function OPSGROUP:onbeforeTaskExecute(From, Event, To, Task)
if self:IsWaiting() then
-- Group is already waiting
else
-- Wait indefinately.
-- Wait indefinitely.
local alt=Mission.missionAltitude and UTILS.MetersToFeet(Mission.missionAltitude) or nil
self:Wait(nil, alt)
end
@@ -4480,7 +4507,7 @@ function OPSGROUP:_UpdateTask(Task, Mission)
-- Set speed. Default max.
local speed=self.speedMax and UTILS.KmphToKnots(self.speedMax) or nil
if Task.dcstask.params.speed then
speed=Task.dcstask.params.speed
speed=UTILS.MpsToKnots(Task.dcstask.params.speed)
end
if target then
@@ -6073,17 +6100,16 @@ function OPSGROUP:RouteToMission(mission, delay)
-- Target Coord.
local targetcoord=mission:GetTargetCoordinate()
-- In range already?
local inRange=self:InWeaponRange(targetcoord, mission.engageWeaponType)
local inRange=self:InWeaponRange(targetcoord, mission.engageWeaponType, waypointcoord)
if inRange then
waypointcoord=self:GetCoordinate(true)
--waypointcoord=self:GetCoordinate(true)
else
local coordInRange=self:GetCoordinateInRange(targetcoord, mission.engageWeaponType, waypointcoord)
local coordInRange=self:GetCoordinateInRange(targetcoord, mission.engageWeaponType, waypointcoord, surfacetypes)
if coordInRange then
@@ -6118,7 +6144,32 @@ function OPSGROUP:RouteToMission(mission, delay)
-- Add mission execution (ingress) waypoint.
local waypoint=nil --#OPSGROUP.Waypoint
if self:IsFlightgroup() then
local ingresscoord = mission:GetMissionIngressCoord()
local holdingcoord = mission:GetMissionHoldingCoord()
if holdingcoord then
waypoint=FLIGHTGROUP.AddWaypoint(self, holdingcoord, mission.missionHoldingCoordSpeed or SpeedToMission, uid, UTILS.MetersToFeet(mission.missionHoldingCoordAlt or self.altitudeCruise), false)
uid=waypoint.uid
-- Orbit until flaghold=1 (true) but max 5 min
self.flaghold:Set(0)
local TaskOrbit = self.group:TaskOrbit(holdingcoord, mission.missionHoldingCoordAlt, UTILS.KnotsToMps(mission.missionHoldingCoordSpeed or SpeedToMission))
local TaskStop = self.group:TaskCondition(nil, self.flaghold.UserFlagName, 1, nil, mission.missionHoldingDuration or 900)
local TaskCntr = self.group:TaskControlled(TaskOrbit, TaskStop)
local TaskOver = self.group:TaskFunction("FLIGHTGROUP._FinishedWaiting", self)
local DCSTasks=self.group:TaskCombo({TaskCntr, TaskOver})
-- Add waypoint task. UpdateRoute is called inside.
local waypointtask=self:AddTaskWaypoint(DCSTasks, waypoint, "Holding")
waypointtask.ismission=false
self.isHoldingAtHoldingPoint = true
end
if ingresscoord then
waypoint=FLIGHTGROUP.AddWaypoint(self, ingresscoord, mission.missionIngressCoordSpeed or SpeedToMission, uid, UTILS.MetersToFeet(mission.missionIngressCoordAlt or self.altitudeCruise), false)
uid=waypoint.uid
end
waypoint=FLIGHTGROUP.AddWaypoint(self, waypointcoord, SpeedToMission, uid, UTILS.MetersToFeet(mission.missionAltitude or self.altitudeCruise), false)
elseif self:IsArmygroup() then
@@ -6157,7 +6208,7 @@ function OPSGROUP:RouteToMission(mission, delay)
if egresscoord then
local Ewaypoint=nil --#OPSGROUP.Waypoint
if self:IsFlightgroup() then
Ewaypoint=FLIGHTGROUP.AddWaypoint(self, egresscoord, SpeedToMission, waypoint.uid, UTILS.MetersToFeet(mission.missionAltitude or self.altitudeCruise), false)
Ewaypoint=FLIGHTGROUP.AddWaypoint(self, egresscoord, mission.missionEgressCoordSpeed or SpeedToMission, waypoint.uid, UTILS.MetersToFeet(mission.missionEgressCoordAlt or self.altitudeCruise), false)
elseif self:IsArmygroup() then
Ewaypoint=ARMYGROUP.AddWaypoint(self, egresscoord, SpeedToMission, waypoint.uid, mission.optionFormation, false)
elseif self:IsNavygroup() then
@@ -7675,6 +7726,7 @@ function OPSGROUP:Teleport(Coordinate, Delay, NoPauseMission)
unit.heading=math.rad(heading)
unit.psi=-unit.heading
else
-- Remove unit from spawn template because it is already dead
table.remove(units, i)
end
end
@@ -7762,25 +7814,41 @@ function OPSGROUP:_Respawn(Delay, Template, Reset)
-- Despawn old group. Dont trigger any remove unit event since this is a respawn.
self:Despawn(0, true)
else
---
-- Group is NOT ALIVE
---
-- Ensure elements in utero.
for _,_element in pairs(self.elements) do
local element=_element --#OPSGROUP.Element
self:ElementInUtero(element)
end
end
-- Ensure elements in utero.
for _,_element in pairs(self.elements) do
local element=_element --#OPSGROUP.Element
if element and element.status~=OPSGROUP.ElementStatus.DEAD then
self:ElementInUtero(element)
end
end
-- Spawn with a little delay (especially Navy groups caused problems if they were instantly respawned)
self:_Spawn(0.01, Template)
end
return self
end
--- Spawn group from a given template.
-- @param #OPSGROUP self
-- @param #number Delay Delay in seconds before respawn happens. Default 0.
-- @param DCS#Template Template (optional) The template of the Group retrieved with GROUP:GetTemplate(). If the template is not provided, the template will be retrieved of the group itself.
-- @return #OPSGROUP self
function OPSGROUP:_Spawn(Delay, Template)
if Delay and Delay>0 then
self:ScheduleOnce(Delay, OPSGROUP._Spawn, self, 0, Template)
else
-- Debug output.
self:T({Template=Template})
self:T2({Template=Template})
-- Spawn new group.
self.group=_DATABASE:Spawn(Template)
--local countryID=self.group:GetCountry()
--local categoryID=self.group:GetCategory()
--local dcsgroup=coalition.addGroup(countryID, categoryID, Template)
-- Set DCS group and controller.
self.dcsgroup=self:GetDCSGroup()
@@ -7794,7 +7862,6 @@ function OPSGROUP:_Respawn(Delay, Template, Reset)
self.isDead=false
self.isDestroyed=false
self.groupinitialized=false
self.wpcounter=1
self.currentwp=1
@@ -7802,15 +7869,12 @@ function OPSGROUP:_Respawn(Delay, Template, Reset)
-- Init waypoints.
self:_InitWaypoints()
-- Init Group.
self:_InitGroup(Template)
-- Init Group. This call is delayed because NAVY groups did not like to be initialized just yet (group did not contain any units).
self:_InitGroup(Template, 0.001)
-- Reset events.
--self:ResetEvents()
--self:ResetEvents()
end
return self
end
--- On after "InUtero" event.
@@ -7830,24 +7894,6 @@ end
-- @param #string To To state.
function OPSGROUP:onafterDamaged(From, Event, To)
self:T(self.lid..string.format("Group damaged at t=%.3f", timer.getTime()))
--[[
local lifemin=nil
for _,_element in pairs(self.elements) do
local element=_element --#OPSGROUP.Element
if element.status~=OPSGROUP.ElementStatus.DEAD and element.status~=OPSGROUP.ElementStatus.INUTERO then
local life, life0=self:GetLifePoints(element)
if lifemin==nil or life<lifemin then
lifemin=life
end
end
end
if lifemin and lifemin/self.life<0.5 then
self:RTB()
end
]]
end
--- On after "Destroyed" event.
@@ -8999,7 +9045,7 @@ function OPSGROUP:AddWeightCargo(UnitName, Weight)
self:T(self.lid..string.format("%s: Adding %.1f kg cargo weight. New cargo weight=%.1f kg", UnitName, Weight, element.weightCargo))
-- For airborne units, we set the weight in game.
if self.isFlightgroup then
if self.isFlightgroup and element.unit and element.unit:IsAlive() then -- #2272 trying to deduct cargo weight from possibly dead units
trigger.action.setUnitInternalCargo(element.name, element.weightCargo) --https://wiki.hoggitworld.com/view/DCS_func_setUnitInternalCargo
end
@@ -11444,10 +11490,10 @@ function OPSGROUP:_InitWaypoints(WpIndexMin, WpIndexMax)
if self:IsFlightgroup() then
-- Get home and destination airbases from waypoints.
self.homebase=self.homebase or self:GetHomebaseFromWaypoints()
self.homebase=self.homebase or self:GetHomebaseFromWaypoints() -- GetHomebaseFromWaypoints() returns carriers or destroyers if no airbase is found.
local destbase=self:GetDestinationFromWaypoints()
self.destbase=self.destbase or destbase
self.currbase=self:GetHomebaseFromWaypoints()
self.currbase=self:GetHomebaseFromWaypoints() -- Skipped To fix RTB issue
--env.info("FF home base "..(self.homebase and self.homebase:GetName() or "unknown"))
--env.info("FF dest base "..(self.destbase and self.destbase:GetName() or "unknown"))
@@ -11458,7 +11504,7 @@ function OPSGROUP:_InitWaypoints(WpIndexMin, WpIndexMax)
end
-- Set destination to homebase.
if self.destbase==nil then
if self.destbase==nil then -- Skipped To fix RTB issue
self.destbase=self.homebase
end

View File

@@ -53,7 +53,8 @@
-- @field #number threatlevelCapture Threat level necessary to capture a zone.
-- @field Core.Set#SET_UNIT ScanUnitSet Set of scanned units.
-- @field Core.Set#SET_GROUP ScanGroupSet Set of scanned groups.
-- @extends Core.Fsm#FSM
-- @field #number UpdateSeconds Run status every this many seconds.
-- @extends Core.Fsm#FSM
--- *Gentlemen, when the enemy is committed to a mistake we must not interrupt him too soon.* --- Horation Nelson
--
@@ -77,6 +78,7 @@ OPSZONE = {
Tnut = 0,
chiefs = {},
Missions = {},
UpdateSeconds = 120,
}
--- OPSZONE.MISSION
@@ -97,7 +99,7 @@ OPSZONE.ZoneType={
--- OPSZONE class version.
-- @field #string version
OPSZONE.version="0.6.1"
OPSZONE.version="0.6.2"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ToDo list
@@ -490,6 +492,19 @@ function OPSZONE:SetDrawZone(Switch)
return self
end
--- Set if zone is drawn on the F10 map for the owner coalition only.
-- @param #OPSZONE self
-- @param #boolean Switch If `false` or `nil`, draw zone for all coalitions. If `true`, zone is drawn for the owning coalition only if drawZone is true.
-- @return #OPSZONE self
function OPSZONE:SetDrawZoneForCoalition(Switch)
if Switch==true then
self.drawZoneForCoalition=true
else
self.drawZoneForCoalition=false
end
return self
end
--- Set if a marker on the F10 map shows the current zone status.
-- @param #OPSZONE self
-- @param #boolean Switch If `true`, zone is marked. If `false` or `nil`, zone is not marked.
@@ -710,6 +725,7 @@ end
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @return #OPSZONE self
function OPSZONE:onafterStart(From, Event, To)
-- Info.
@@ -719,13 +735,15 @@ function OPSZONE:onafterStart(From, Event, To)
self.timerStatus=self.timerStatus or TIMER:New(OPSZONE.Status, self)
-- Status update.
self.timerStatus:Start(1, 120)
local EveryUpdateIn = self.UpdateSeconds or 120
self.timerStatus:Start(1, EveryUpdateIn)
-- Handle base captured event.
if self.airbase then
self:HandleEvent(EVENTS.BaseCaptured)
end
return self
end
--- Stop OPSZONE FSM.
@@ -837,8 +855,12 @@ function OPSZONE:onafterCaptured(From, Event, To, NewOwnerCoalition)
self.zone:UndrawZone()
local color=self:_GetZoneColor()
self.zone:DrawZone(nil, color, 1.0, color, 0.5)
local coalition = nil
if self.drawZoneForCoalition then
coalition = self.ownerCurrent
end
self.zone:DrawZone(coalition, color, 1.0, color, 0.5)
end
for _,_chief in pairs(self.chiefs) do
@@ -913,8 +935,12 @@ function OPSZONE:onenterGuarded(From, Event, To)
self.zone:UndrawZone()
local color=self:_GetZoneColor()
self.zone:DrawZone(nil, color, 1.0, color, 0.5)
local coalition = nil
if self.drawZoneForCoalition then
coalition = self.ownerCurrent
end
self.zone:DrawZone(coalition, color, 1.0, color, 0.5)
end
end
@@ -954,9 +980,13 @@ function OPSZONE:onenterAttacked(From, Event, To, AttackerCoalition)
-- Color.
local color={1, 204/255, 204/255}
local coalition = nil
if self.drawZoneForCoalition then
coalition = self.ownerCurrent
end
-- Draw zone.
self.zone:DrawZone(nil, color, 1.0, color, 0.5)
self.zone:DrawZone(coalition, color, 1.0, color, 0.5)
end
self:_CleanMissionTable()
@@ -987,8 +1017,12 @@ function OPSZONE:onenterEmpty(From, Event, To)
self.zone:UndrawZone()
local color=self:_GetZoneColor()
self.zone:DrawZone(nil, color, 1.0, color, 0.2)
local coalition = nil
if self.drawZoneForCoalition then
coalition = self.ownerCurrent
end
self.zone:DrawZone(coalition, color, 1.0, color, 0.2)
end
end
@@ -1285,7 +1319,7 @@ function OPSZONE:EvaluateZone()
if Nblu>0 then
if not self:IsAttacked() and self.Tnut>=self.threatlevelCapture then
if not self:IsAttacked() and self.Tblu>=self.threatlevelCapture then
self:Attacked(coalition.side.BLUE)
end
@@ -1337,7 +1371,7 @@ function OPSZONE:EvaluateZone()
if Nred>0 then
if not self:IsAttacked() and self.Tnut>=self.threatlevelCapture then
if not self:IsAttacked() and self.Tred>=self.threatlevelCapture then
-- Red is attacking blue zone.
self:Attacked(coalition.side.RED)
end

View File

@@ -80,6 +80,7 @@
-- @field #boolean smokeownposition
-- @field #table SmokeOwn
-- @field #boolean smokeaveragetargetpos
-- @field #boolean reporttostringbullsonly
-- @extends Core.Fsm#FSM
---
@@ -105,7 +106,7 @@ PLAYERRECCE = {
ClassName = "PLAYERRECCE",
verbose = true,
lid = nil,
version = "0.1.23",
version = "0.1.26",
ViewZone = {},
ViewZoneVisual = {},
ViewZoneLaser = {},
@@ -133,7 +134,8 @@ PLAYERRECCE = {
TargetCache = nil,
smokeownposition = false,
SmokeOwn = {},
smokeaveragetargetpos = false,
smokeaveragetargetpos = true,
reporttostringbullsonly = true,
}
---
@@ -152,7 +154,8 @@ PLAYERRECCE.LaserRelativePos = {
["SA342Minigun"] = { x = 1.7, y = 1.2, z = 0 },
["SA342L"] = { x = 1.7, y = 1.2, z = 0 },
["Ka-50"] = { x = 6.1, y = -0.85 , z = 0 },
["Ka-50_3"] = { x = 6.1, y = -0.85 , z = 0 }
["Ka-50_3"] = { x = 6.1, y = -0.85 , z = 0 },
["OH58D"] = {x = 0, y = 2.8, z = 0},
}
---
@@ -164,7 +167,8 @@ PLAYERRECCE.MaxViewDistance = {
["SA342Minigun"] = 8000,
["SA342L"] = 8000,
["Ka-50"] = 8000,
["Ka-50_3"] = 8000,
["Ka-50_3"] = 8000,
["OH58D"] = 8000,
}
---
@@ -176,7 +180,8 @@ PLAYERRECCE.Cameraheight = {
["SA342Minigun"] = 2.85,
["SA342L"] = 2.85,
["Ka-50"] = 0.5,
["Ka-50_3"] = 0.5,
["Ka-50_3"] = 0.5,
["OH58D"] = 4.25,
}
---
@@ -188,7 +193,8 @@ PLAYERRECCE.CanLase = {
["SA342Minigun"] = false, -- no optics
["SA342L"] = true,
["Ka-50"] = true,
["Ka-50_3"] = true,
["Ka-50_3"] = true,
["OH58D"] = false, -- has onboard and useable laser
}
---
@@ -236,6 +242,8 @@ function PLAYERRECCE:New(Name, Coalition, PlayerSet)
self.minthreatlevel = 0
self.reporttostringbullsonly = true
self.TForget = 600
self.TargetCache = FIFO:New()
@@ -542,7 +550,7 @@ function PLAYERRECCE:SetAttackSet(AttackSet)
return self
end
---[Internal] Check Gazelle camera in on
---[Internal] Check Helicopter camera in on
-- @param #PLAYERRECCE self
-- @param Wrapper.Client#CLIENT client
-- @param #string playername
@@ -558,6 +566,12 @@ function PLAYERRECCE:_CameraOn(client,playername)
if vivihorizontal < -0.7 or vivihorizontal > 0.7 then
camera = false
end
elseif string.find(typename,"OH58") then
local dcsunit = Unit.getByName(client:GetName())
local vivihorizontal = dcsunit:getDrawArgumentValue(528) or 0 -- Kiow
if vivihorizontal < -0.527 or vivihorizontal > 0.527 then
camera = false
end
elseif string.find(typename,"Ka-50") then
camera = true
end
@@ -565,6 +579,52 @@ function PLAYERRECCE:_CameraOn(client,playername)
return camera
end
--- [Internal] Get the view parameters from a Kiowa MMS camera
-- @param #PLAYERRECCE self
-- @param Wrapper.Unit#UNIT Kiowa
-- @return #number cameraheading in degrees.
-- @return #number cameranodding in degrees.
-- @return #number maxview in meters.
-- @return #boolean cameraison If true, camera is on, else off.
function PLAYERRECCE:_GetKiowaMMSSight(Kiowa)
self:T(self.lid.."_GetKiowaMMSSight")
local unit = Kiowa -- Wrapper.Unit#UNIT
if unit and unit:IsAlive() then
local dcsunit = Unit.getByName(Kiowa:GetName())
--[[
shagrat — 01/01/2025 23:13
Found the necessary ARGS for the Kiowa MMS angle and rotation:
Arg 527 vertical movement
0 = neutral
-1.0 = max depression (30° max depression angle)
+1.0 = max elevation angle (30° max elevation angle)
Arg 528 horizontal movement
0 = forward (0 degr)
-0.25 = 90° left
-0.5 = rear (180°) left (max 190° = -0.527
+0.25 = 90° right
+0.5 = 180° right (max 190° = 0.527)
--]]
local mmshorizontal = dcsunit:getDrawArgumentValue(528) or 0
local mmsvertical = dcsunit:getDrawArgumentValue(527) or 0
self:T(string.format("Kiowa MMS Arguments Read: H %.3f V %.3f",mmshorizontal,mmsvertical))
local mmson = true
if mmshorizontal < -0.527 or mmshorizontal > 0.527 then mmson = false end
local horizontalview = mmshorizontal / 0.527 * 190
local heading = unit:GetHeading()
local mmsheading = (heading+horizontalview)%360
--local mmsyaw = mmsvertical * 30
local mmsyaw = math.atan(mmsvertical)*40
local maxview = self:_GetActualMaxLOSight(unit,mmsheading, mmsyaw,not mmson)
if maxview > 8000 then maxview = 8000 end
self:T(string.format("Kiowa MMS Heading %d, Yaw %d, MaxView %dm MMS On %s",mmsheading,mmsyaw,maxview,tostring(mmson)))
return mmsheading,mmsyaw,maxview,mmson
end
return 0,0,0,false
end
--- [Internal] Get the view parameters from a Gazelle camera
-- @param #PLAYERRECCE self
-- @param Wrapper.Unit#UNIT Gazelle
@@ -593,40 +653,15 @@ function PLAYERRECCE:_GetGazelleVivianneSight(Gazelle)
vivioff = true
return 0,0,0,false
end
vivivertical = vivivertical / 1.10731 -- normalize
local horizontalview = vivihorizontal * -180
local verticalview = vivivertical * 30 -- ca +/- 30°
--self:I(string.format("vivihorizontal=%.5f | vivivertical=%.5f",vivihorizontal,vivivertical))
--self:I(string.format("horizontal=%.5f | vertical=%.5f",horizontalview,verticalview))
--local verticalview = vivivertical * 30 -- ca +/- 30°
local verticalview = math.atan(vivivertical)
local heading = unit:GetHeading()
local viviheading = (heading+horizontalview)%360
local maxview = self:_GetActualMaxLOSight(unit,viviheading, verticalview,vivioff)
--self:I(string.format("maxview=%.5f",maxview))
-- visual skew
local factor = 3.15
self.GazelleViewFactors = {
[1]=1.18,
[2]=1.32,
[3]=1.46,
[4]=1.62,
[5]=1.77,
[6]=1.85,
[7]=2.05,
[8]=2.05,
[9]=2.3,
[10]=2.3,
[11]=2.27,
[12]=2.27,
[13]=2.43,
}
local lfac = UTILS.Round(maxview,-2)
if lfac <= 1300 then
--factor = self.GazelleViewFactors[lfac/100]
factor = 3.15
maxview = math.ceil((maxview*factor)/100)*100
end
if maxview > 8000 then maxview = 8000 end
--self:I(string.format("corrected maxview=%.5f",maxview))
return viviheading, verticalview,maxview, not vivioff
end
return 0,0,0,false
@@ -647,20 +682,20 @@ function PLAYERRECCE:_GetActualMaxLOSight(unit,vheading, vnod, vivoff)
if unit and unit:IsAlive() then
local typename = unit:GetTypeName()
maxview = self.MaxViewDistance[typename] or 8000
local CamHeight = self.Cameraheight[typename] or 0
if vnod < 0 then
local CamHeight = self.Cameraheight[typename] or 1
if vnod < -2 then
-- Looking down
-- determine max distance we're looking at
local beta = 90
local gamma = math.floor(90-vnod)
local alpha = math.floor(180-beta-gamma)
local gamma = 90-math.abs(vnod)
local alpha = 90-gamma
local a = unit:GetHeight()-unit:GetCoordinate():GetLandHeight()+CamHeight
local b = a / math.sin(math.rad(alpha))
local c = b * math.sin(math.rad(gamma))
maxview = c*1.2 -- +20%
end
end
return math.abs(maxview)
return math.ceil(math.abs(maxview))
end
--- [User] Set callsign options for TTS output. See @{Wrapper.Group#GROUP.GetCustomCallSign}() on how to set customized callsigns.
@@ -669,8 +704,10 @@ end
-- @param #boolean Keepnumber If true, keep the **customized callsign** in the #GROUP name for players as-is, no amendments or numbers.
-- @param #table CallsignTranslations (optional) Table to translate between DCS standard callsigns and bespoke ones. Does not apply if using customized
-- callsigns from playername or group name.
-- @param #func CallsignCustomFunc (Optional) For player names only(!). If given, this function will return the callsign. Needs to take the groupname and the playername as first two arguments.
-- @param #arg ... (Optional) Comma separated arguments to add to the custom function call after groupname and playername.
-- @return #PLAYERRECCE self
function PLAYERRECCE:SetCallSignOptions(ShortCallsign,Keepnumber,CallsignTranslations)
function PLAYERRECCE:SetCallSignOptions(ShortCallsign,Keepnumber,CallsignTranslations,CallsignCustomFunc,...)
if not ShortCallsign or ShortCallsign == false then
self.ShortCallsign = false
else
@@ -678,6 +715,8 @@ function PLAYERRECCE:SetCallSignOptions(ShortCallsign,Keepnumber,CallsignTransla
end
self.Keepnumber = Keepnumber or false
self.CallsignTranslations = CallsignTranslations
self.CallsignCustomFunc = CallsignCustomFunc
self.CallsignCustomArgs = arg or {}
return self
end
@@ -799,7 +838,7 @@ function PLAYERRECCE:_GetTargetSet(unit,camera,laser)
local minview = 0
local typename = unit:GetTypeName()
local playername = unit:GetPlayerName()
local maxview = self.MaxViewDistance[typename] or 5000
local maxview = self.MaxViewDistance[typename] or 8000
local heading,nod,maxview,angle = 0,30,8000,10
local camon = false
local name = unit:GetName()
@@ -807,16 +846,25 @@ function PLAYERRECCE:_GetTargetSet(unit,camera,laser)
heading,nod,maxview,camon = self:_GetGazelleVivianneSight(unit)
angle=10
-- Model nod and actual TV view don't compute
maxview = self.MaxViewDistance[typename] or 5000
maxview = self.MaxViewDistance[typename] or 8000
elseif string.find(typename,"Ka-50") and camera then
heading = unit:GetHeading()
nod,maxview,camon = 10,1000,true
angle = 10
maxview = self.MaxViewDistance[typename] or 5000
maxview = self.MaxViewDistance[typename] or 8000
elseif string.find(typename,"OH58") and camera then
--heading = unit:GetHeading()
nod,maxview,camon = 0,8000,true
heading,nod,maxview,camon = self:_GetKiowaMMSSight(unit)
angle = 8
if maxview == 0 then
maxview = self.MaxViewDistance[typename] or 8000
end
else
-- visual
heading = unit:GetHeading()
nod,maxview,camon = 10,1000,true
nod,maxview,camon = 10,3000,true
maxview = self.MaxViewDistance[typename] or 3000
angle = 45
end
if laser then
@@ -929,7 +977,8 @@ function PLAYERRECCE:_LaseTarget(client,targetset)
if (not oldtarget) or targetset:IsNotInSet(oldtarget) or target:IsDead() or target:IsDestroyed() then
-- lost LOS or dead
laser:LaseOff()
if target:IsDead() or target:IsDestroyed() or target:GetLife() < 2 then
self:T(self.lid.."Target Life Points: "..target:GetLife() or "none")
if target:IsDead() or target:IsDestroyed() or target:GetDamage() > 79 or target:GetLife() <= 1 then
self:__Shack(-1,client,oldtarget)
--self.LaserTarget[playername] = nil
else
@@ -1274,6 +1323,9 @@ self:T(self.lid.."_ReportLaserTargets")
report:Add("Threat Level: "..ThreatGraph.." ("..ThreatLevelText..")")
if not self.ReferencePoint then
report:Add("Location: "..client:GetCoordinate():ToStringBULLS(self.Coalition,Settings))
if self.reporttostringbullsonly ~= true then
report:Add("Location: "..client:GetCoordinate():ToStringA2G(nil,Settings))
end
else
report:Add("Location: "..client:GetCoordinate():ToStringFromRPShort(self.ReferencePoint,self.RPName,client,Settings))
end
@@ -1317,8 +1369,14 @@ function PLAYERRECCE:_ReportVisualTargets(client,group,playername)
report:Add("Threat Level: "..ThreatGraph.." ("..ThreatLevelText..")")
if not self.ReferencePoint then
report:Add("Location: "..client:GetCoordinate():ToStringBULLS(self.Coalition,Settings))
if self.reporttostringbullsonly ~= true then
report:Add("Location: "..client:GetCoordinate():ToStringA2G(nil,Settings))
end
else
report:Add("Location: "..client:GetCoordinate():ToStringFromRPShort(self.ReferencePoint,self.RPName,client,Settings))
if self.reporttostringbullsonly ~= true then
report:Add("Location: "..client:GetCoordinate():ToStringA2G(nil,Settings))
end
end
report:Add(string.rep("-",15))
local text = report:Text()
@@ -1347,6 +1405,7 @@ function PLAYERRECCE:_BuildMenus(Client)
local client = _client -- Wrapper.Client#CLIENT
if client and client:IsAlive() then
local playername = client:GetPlayerName()
self:T("Menu for "..playername)
if not self.UnitLaserCodes[playername] then
self:_SetClientLaserCode(nil,nil,playername,1688)
end
@@ -1355,6 +1414,7 @@ function PLAYERRECCE:_BuildMenus(Client)
end
local group = client:GetGroup()
if not self.ClientMenus[playername] then
self:T("Start Menubuild for "..playername)
local canlase = self.CanLase[client:GetTypeName()]
self.ClientMenus[playername] = MENU_GROUP:New(group,self.MenuName or self.Name or "RECCE")
local txtonstation = self.OnStation[playername] and "ON" or "OFF"
@@ -1492,8 +1552,9 @@ end
-- Note that this must be installed on your windows system. Can also be Google voice types, if you are using Google TTS.
-- @param #number Volume (Optional) Volume - between 0.0 (silent) and 1.0 (loudest)
-- @param #string PathToGoogleKey (Optional) Path to your google key if you want to use google TTS
-- @param #string Backend (optional) Backend to be used, can be MSRS.Backend.SRSEXE or MSRS.Backend.GRPC
-- @return #PLAYERRECCE self
function PLAYERRECCE:SetSRS(Frequency,Modulation,PathToSRS,Gender,Culture,Port,Voice,Volume,PathToGoogleKey)
function PLAYERRECCE:SetSRS(Frequency,Modulation,PathToSRS,Gender,Culture,Port,Voice,Volume,PathToGoogleKey,Backend)
self:T(self.lid.."SetSRS")
self.PathToSRS = PathToSRS or MSRS.path or "C:\\Program Files\\DCS-SimpleRadio-Standalone" --
self.Gender = Gender or MSRS.gender or "male" --
@@ -1515,6 +1576,9 @@ function PLAYERRECCE:SetSRS(Frequency,Modulation,PathToSRS,Gender,Culture,Port,V
self.SRS:SetCulture(self.Culture)
self.SRS:SetPort(self.Port)
self.SRS:SetVolume(self.Volume)
if Backend then
self.SRS:SetBackend(Backend)
end
if self.PathToGoogleKey then
self.SRS:SetProviderOptionsGoogle(self.PathToGoogleKey,self.PathToGoogleKey)
self.SRS:SetProvider(MSRS.Provider.GOOGLE)
@@ -1552,6 +1616,16 @@ function PLAYERRECCE:SetMenuName(Name)
return self
end
--- [User] Set reporting to be BULLS only or BULLS plus playersettings based coordinate.
-- @param #PLAYERRECCE self
-- @param #boolean OnOff
-- @return #PLAYERRECCE self
function PLAYERRECCE:SetReportBullsOnly(OnOff)
self:T(self.lid.."SetReportBullsOnly: "..tostring(OnOff))
self.reporttostringbullsonly = OnOff
return self
end
--- [User] Enable smoking of own position
-- @param #PLAYERRECCE self
-- @return #PLAYERRECCE self
@@ -1561,6 +1635,15 @@ function PLAYERRECCE:EnableSmokeOwnPosition()
return self
end
--- [User] Enable auto lasing for the Kiowa OH-58D.
-- @param #PLAYERRECCE self
-- @return #PLAYERRECCE self
function PLAYERRECCE:EnableKiowaAutolase()
self:T(self.lid.."EnableKiowaAutolase")
self.CanLase.OH58D = true
return self
end
--- [User] Disable smoking of own position
-- @param #PLAYERRECCE self
-- @return #PLAYERRECCE
@@ -1718,7 +1801,7 @@ end
-- @return #PLAYERRECCE self
function PLAYERRECCE:onafterRecceOnStation(From, Event, To, Client, Playername)
self:T({From, Event, To})
local callsign = Client:GetGroup():GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations)
local callsign = Client:GetGroup():GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations,self.CallsignCustomFunc,self.CallsignCustomArgs)
local coord = Client:GetCoordinate()
local coordtext = coord:ToStringBULLS(self.Coalition)
if self.ReferencePoint then
@@ -1727,7 +1810,7 @@ function PLAYERRECCE:onafterRecceOnStation(From, Event, To, Client, Playername)
end
local text1 = "Party time!"
local text2 = string.format("All stations, FACA %s on station\nat %s!",callsign, coordtext)
local text2tts = string.format("All stations, FACA %s on station at %s!",callsign, coordtext)
local text2tts = string.format(" All stations, FACA %s on station at %s!",callsign, coordtext)
text2tts = self:_GetTextForSpeech(text2tts)
if self.debug then
self:T(text2.."\n"..text2tts)
@@ -1758,7 +1841,7 @@ end
-- @return #PLAYERRECCE self
function PLAYERRECCE:onafterRecceOffStation(From, Event, To, Client, Playername)
self:T({From, Event, To})
local callsign = Client:GetGroup():GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations)
local callsign = Client:GetGroup():GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations,self.CallsignCustomFunc,self.CallsignCustomArgs)
local coord = Client:GetCoordinate()
local coordtext = coord:ToStringBULLS(self.Coalition)
if self.ReferencePoint then
@@ -1898,7 +1981,7 @@ end
-- @return #PLAYERRECCE self
function PLAYERRECCE:onafterIllumination(From, Event, To, Client, Playername, TargetSet)
self:T({From, Event, To})
local callsign = Client:GetGroup():GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations)
local callsign = Client:GetGroup():GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations,self.CallsignCustomFunc,self.CallsignCustomArgs)
local coord = Client:GetCoordinate()
local coordtext = coord:ToStringBULLS(self.Coalition)
if self.AttackSet then
@@ -1941,7 +2024,7 @@ end
-- @return #PLAYERRECCE self
function PLAYERRECCE:onafterTargetsSmoked(From, Event, To, Client, Playername, TargetSet)
self:T({From, Event, To})
local callsign = Client:GetGroup():GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations)
local callsign = Client:GetGroup():GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations,self.CallsignCustomFunc,self.CallsignCustomArgs)
local coord = Client:GetCoordinate()
local coordtext = coord:ToStringBULLS(self.Coalition)
if self.AttackSet then
@@ -1984,7 +2067,7 @@ end
-- @return #PLAYERRECCE self
function PLAYERRECCE:onafterTargetsFlared(From, Event, To, Client, Playername, TargetSet)
self:T({From, Event, To})
local callsign = Client:GetGroup():GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations)
local callsign = Client:GetGroup():GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations,self.CallsignCustomFunc,self.CallsignCustomArgs)
local coord = Client:GetCoordinate()
local coordtext = coord:ToStringBULLS(self.Coalition)
if self.AttackSet then
@@ -2028,7 +2111,7 @@ end
-- @return #PLAYERRECCE self
function PLAYERRECCE:onafterTargetLasing(From, Event, To, Client, Target, Lasercode, Lasingtime)
self:T({From, Event, To})
local callsign = Client:GetGroup():GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations)
local callsign = Client:GetGroup():GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations,self.CallsignCustomFunc,self.CallsignCustomArgs)
local Settings = ( Client and _DATABASE:GetPlayerSettings( Client:GetPlayerName() ) ) or _SETTINGS
local coord = Client:GetCoordinate()
local coordtext = coord:ToStringBULLS(self.Coalition,Settings)
@@ -2075,7 +2158,7 @@ end
-- @return #PLAYERRECCE self
function PLAYERRECCE:onafterShack(From, Event, To, Client, Target)
self:T({From, Event, To})
local callsign = Client:GetGroup():GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations)
local callsign = Client:GetGroup():GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations,self.CallsignCustomFunc,self.CallsignCustomArgs)
local Settings = ( Client and _DATABASE:GetPlayerSettings( Client:GetPlayerName() ) ) or _SETTINGS
local coord = Client:GetCoordinate()
local coordtext = coord:ToStringBULLS(self.Coalition,Settings)
@@ -2122,7 +2205,7 @@ end
-- @return #PLAYERRECCE self
function PLAYERRECCE:onafterTargetLOSLost(From, Event, To, Client, Target)
self:T({From, Event, To})
local callsign = Client:GetGroup():GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations)
local callsign = Client:GetGroup():GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations,self.CallsignCustomFunc,self.CallsignCustomArgs)
local Settings = ( Client and _DATABASE:GetPlayerSettings( Client:GetPlayerName() ) ) or _SETTINGS
local coord = Client:GetCoordinate()
local coordtext = coord:ToStringBULLS(self.Coalition,Settings)

File diff suppressed because it is too large Load Diff

View File

@@ -997,6 +997,8 @@ function RECOVERYTANKER:onafterStart(From, Event, To)
-- Init status updates in 10 seconds.
self:__Status(10)
return self
end

View File

@@ -963,6 +963,8 @@ function RESCUEHELO:onafterStart(From, Event, To)
-- Init status check
self:__Status(1)
return self
end
--- On after Status event. Checks player status.

Some files were not shown because too many files have changed in this diff Show More