Compare commits

...

141 Commits
2.3.0 ... 2.4.a

Author SHA1 Message Date
FlightControl_Master
0e0ab3507c Okay, fixed the problem with the crashing at the deploy zone.
Also added methods to set and/or randomize the pickup speed, pickup radius, deploy speed, deploy radius.
2018-05-13 15:57:28 +02:00
Frank
92a70d07d9 Merge pull request #899 from FlightControl-Master/FF/Develop
ARTY v0.9.1
2018-05-12 15:44:58 +02:00
funkyfranky
8db5351ad7 ARTY v0.9.1
Added option to not engage an already assigned target.
Fixed bug in _checkname function.
2018-05-12 15:42:15 +02:00
FlightControl_Master
25a6cbcf6d Helicopter AI_CARGO_DISPATCHER working (almost).
Queueing at deploy locations working.
2018-05-12 08:37:27 +02:00
Frank
ac7ae68ee8 Merge pull request #898 from FlightControl-Master/FF/Develop
Added Big Smoke Effect
2018-05-11 21:56:01 +02:00
funkyfranky
230a803045 Corrected description. 2018-05-11 12:30:33 +02:00
funkyfranky
d61840d834 Added Big Smoke Effect
Added big smoke effects to COORDINATE API.
2018-05-11 12:27:55 +02:00
funkyfranky
f74520afd3 Merge branch 'develop' into FF/Develop 2018-05-11 12:26:25 +02:00
FlightControl_Master
74668bb7db Fixes 2018-05-10 23:42:14 +02:00
FlightControl_Master
bca964aca8 Helicopter version of AI_CARGO_DISPATCHER_HELICOPTER 2018-05-10 23:17:06 +02:00
funkyfranky
ad9e97f192 Merge branch 'develop' into FF/Develop 2018-05-10 22:48:29 +02:00
funkyfranky
aac5efbf61 ARTY added shots to report output 2018-05-10 22:48:14 +02:00
FlightControl_Master
6b31ad9645 Moose file changes 2018-05-10 20:00:50 +02:00
Frank
0097316333 Merge pull request #896 from FlightControl-Master/FF/Develop
New classes.
2018-05-10 17:57:21 +02:00
funkyfranky
a0adca43f1 Merge branch 'develop' into FF/Develop 2018-05-10 17:05:45 +02:00
funkyfranky
6f0507ea7f Minor fixes.
Positionalble: Added isExist because of problems with getting coords from scenery objects.
Suppresson: Fixes. Added new transitions.
PseudoATC: Removed eject.
Artillery: Optimized debug output.
2018-05-10 15:40:10 +02:00
FlightControl_Master
1159d11a12 New AI_CARGO_DISPATCHER_APC 2018-05-10 14:39:22 +02:00
FlightControl_Master
9ee21f80ac comments 2018-05-10 08:07:28 +02:00
funkyfranky
f9d7eea721 ARTY, PseudoATC, RANGE, RAT, SUPPRESSION
ARTY v0.9.0: Added anti-ship missiles.  Various fixes.
PSEUDOATC v0.9.0: Added docu.  Cleaned up code.  Bug fixes.
RANGE v1.1.1: Changed menu.
RAT v2.2.2:  Changed default setting to menu = off. Added user function to enable/disable menus.
SUPPRESSION v0.9.0: Improvements.
2018-05-09 23:56:55 +02:00
funkyfranky
191fcb25be PseudoATC v0.7.0
Pseudo ATC improvements.
Minor changes in RAT and Suppression.
2018-05-09 00:11:53 +02:00
FlightControl_Master
a1eb0ff4d9 Fixes 2018-05-08 20:26:46 +02:00
FlightControl_Master
0600c96282 Finish Cargo/AI_Cargo_APC 2018-05-08 06:43:15 +02:00
funkyfranky
95d7b8250d Minor Changes. 2018-05-07 23:32:57 +02:00
funkyfranky
fc852e0386 Merge branch 'develop' into FF/Develop 2018-05-07 17:52:33 +02:00
funkyfranky
9429ec66ca RAT fixed typo 2018-05-07 17:44:53 +02:00
funkyfranky
92c3b530cc ARTY v0.8.9
Improved documentation.
2018-05-07 17:33:48 +02:00
FlightControl_Master
dd636939bb Documentation and performance fix. 2018-05-07 06:11:58 +02:00
funkyfranky
6554fa55d1 ARTY v0.8.8
Minor Changes.
2018-05-07 00:20:19 +02:00
funkyfranky
3157a63b7e ARTY v0.8.8
Added working implementation for moves.
Other minor adjustments.
2018-05-06 20:40:14 +02:00
funkyfranky
d9222c23cb ARTY v0.8.7
Added first version of move implementation. Moves are not executed yet.
2018-05-06 17:03:31 +02:00
funkyfranky
16d4a65569 ARTY v0.8.6
Added user function for rearming properties.
Minor bug fixes.
2018-05-06 15:13:36 +02:00
funkyfranky
a2c12dc05e ARTY v0.8.5
Improved GetAmmo function. Display of ammo table.
Removed all schedulers.
2018-05-06 12:51:51 +02:00
funkyfranky
c36579f88a ARTY v0.8.4
Several improvments and fixes.
2018-05-06 12:05:23 +02:00
FlightControl_Master
19b3dcec21 # Conflicts:
#	Moose Development/Moose/AI/AI_Cargo_APC.lua
#	Moose Development/Moose/Wrapper/Controllable.lua
2018-05-06 07:36:58 +02:00
funkyfranky
ba944444da ARTY v0.8.3
Various improvements. Still WIP.
2018-05-05 23:59:02 +02:00
funkyfranky
f33856cddd ARTY v0.8.2
Fixes. Stil WIP
2018-05-05 22:51:43 +02:00
funkyfranky
57c5ab1ecd ARTY v0.8.1
Improvements in FSM states.
Still not quite functional.
2018-05-05 21:45:07 +02:00
funkyfranky
0cf4b8845e ARTY v0.8.0
WIP version. Not functional.
2018-05-05 08:44:16 +02:00
funkyfranky
4fccfa38d4 ARTY v0.7
Added "NewTarget" event.
Improved task function for waypoints
Removed TargetQueue scheduler.
2018-05-03 23:42:03 +02:00
funkyfranky
531c1d7e90 ARTY v0.6 (WIP)
Added documentation
Added user FSM functions.
Adjusted FSM (untested)
Added task route function.
2018-05-03 00:24:23 +02:00
FlightControl_Master
810d900d7e Working with Transporting mode on or off. 2018-05-02 22:18:07 +02:00
funkyfranky
274b44459e ARTY v0.5
- Adjusted schedulers.
- Improved transitions.
- Make rearming unit go back to its original position.
- Added user functions.
- Using only coodinates for target assignments.
- Added dead event handling.
2018-05-02 00:14:05 +02:00
funkyfranky
394b5f5b89 ARTY v0.4.0
Improved assigned time for engagement. Next day should be possible now.
Added check whether a group started firing within a certain time.
2018-04-30 22:53:57 +02:00
funkyfranky
e5268a29cf ARTY v0.3
First working version. But still WIP.
2018-04-30 12:05:51 +02:00
funkyfranky
b14a672b0e ARTY improvements. 2018-04-26 23:08:16 +02:00
funkyfranky
0ec3192fb7 Arty improvements 2018-04-25 22:49:36 +02:00
funkyfranky
33271edf78 Added ARTY class
First draft...
2018-04-24 23:38:41 +02:00
funkyfranky
441fba0830 PseudoATC
Fixed waypoints BR
2018-04-24 21:12:35 +02:00
funkyfranky
7dbc9436ed PseudoATC
restructured menu
2018-04-24 00:29:09 +02:00
FlightControl_Master
c999389cda Handler for unloading too. 2018-04-23 06:51:49 +02:00
funkyfranky
462564cd01 PseudoATC and RANGE
Range: corrected heading
PseudoATC: lots of changes
2018-04-22 23:55:21 +02:00
funkyfranky
c2a968d2ef Merge branch 'develop' into FF/Develop 2018-04-20 00:13:49 +02:00
funkyfranky
2297646873 Range v1.1.1
Added strafe pit/bombing targets info
2018-04-20 00:13:30 +02:00
FlightControl_Master
af050629aa Made it. Now cargo can be defined in the Mission Editor as #CARGO 2018-04-17 06:25:40 +02:00
FlightControl_Master
3757eb06d9 Fixes for AI_CARGO_AIRPLANE 2018-04-15 04:41:52 +02:00
funkyfranky
06b555a50a Merge branch 'develop' into FF/Develop 2018-04-14 19:35:09 +02:00
FlightControl_Master
b9eab34d6a WIP on AI_Cargo_Airplane 2018-04-14 13:45:00 +02:00
FlightControl_Master
1444a613c5 New AI_Cargo_Airplane file 2018-04-14 12:02:48 +02:00
FlightControl_Master
ecf3434c3d Merge branch 'master' into develop 2018-04-14 08:08:01 +02:00
FlightControl_Master
5988ceec05 A new AI Cargo Helicopter class. 2018-04-13 22:31:19 +02:00
FlightControl_Master
f01b2c9149 Finish Cargo/AI_Cargo_APC 2018-04-13 20:35:38 +02:00
FlightControl_Master
b33dd94fa0 Fixed a bug in Spawn regarding spawning with templates. 2018-04-13 13:57:47 +02:00
FlightControl_Master
47dd73a377 Fixes 2018-04-13 12:22:39 +02:00
FlightControl_Master
25ae0c3d15 Finish this feature. 2018-04-13 09:56:00 +02:00
FlightControl_Master
3389c908d7 Finish Task_Assignment 2018-04-12 20:04:19 +02:00
FlightControl_Master
3e6017c0d5 Task Assignment 2018-04-12 20:03:44 +02:00
FlightControl_Master
fea2f55fbb Lots of optimizations 2018-04-12 13:59:55 +02:00
FlightControl_Master
dcc42b8e62 Fixes 2018-04-11 23:24:13 +02:00
FlightControl_Master
9cea486fdc Progress 2018-04-11 17:09:59 +02:00
FlightControl_Master
faa934ffce Fixed stuff for multi player 2018-04-11 17:00:11 +02:00
FlightControl_Master
1ae1b9abd7 Ready for another test session. 2018-04-11 09:14:12 +02:00
FlightControl_Master
ffe4d9a143 Finish Cargo/FC/Transport 2018-04-10 20:02:40 +02:00
funkyfranky
d5d0703502 Merge branch 'develop' into FF/Develop 2018-04-09 13:57:40 +02:00
funkyfranky
fe3e91d1a0 Merge branch 'master' into FF/Develop 2018-04-09 13:57:17 +02:00
FlightControl_Master
1beb34231e Getting rid of an old annoyance. 2018-04-09 13:38:32 +02:00
FlightControl_Master
a6889be676 # Conflicts:
#	Moose Development/Moose/Tasking/Task_CARGO.lua
2018-04-09 13:30:33 +02:00
FlightControl_Master
4eb596f5bf Finish Cargo/FC/Transport 2018-04-09 12:56:11 +02:00
funkyfranky
01de126a8f Merge branch 'develop' into FF/Develop 2018-04-09 08:16:10 +02:00
FlightControl_Master
86cc1f81b6 Finish Cargo/FC/Transport 2018-04-08 22:38:45 +02:00
FlightControl_Master
a95afe9956 # Conflicts:
#	Moose Development/Moose/Tasking/Task_CARGO.lua
2018-04-08 22:36:58 +02:00
FlightControl_Master
8b8fcaaacd # Conflicts:
#	Moose Development/Moose/Tasking/Task_CARGO.lua
2018-04-08 18:59:42 +02:00
FlightControl_Master
31f0bb9fef Adding events for Deployed and PickedUp. 2018-04-08 13:33:18 +02:00
FlightControl_Master
af23aa3b79 -- Lots of fixes done. Especially on the
- Messaging
- Menu system
- Crashing DCS
- Routing
2018-04-08 11:01:46 +02:00
funkyfranky
e606ab1b8a Merge branch 'develop' into FF/Develop 2018-04-07 23:58:40 +02:00
funkyfranky
303f5cb571 RANGE v1.1.0
- Missiles are now tracked.
- Statics can now be used for strafe pits.
- Statics are automatically recognized.
- More user functions to add bomb or strafe targets.
- Bomb targets are allowed to move.
- Bomb targets can automatically move randomly inside the range zone.
- Improved trace output.
- Documentation updated.
2018-04-07 23:36:09 +02:00
FlightControl_Master
b1ecdc727c Faster menu and fix for Disembark loaded engineers in other helicopter. 2018-04-07 19:00:18 +02:00
FlightControl_Master
7735120f25 Progress on AI_CARGO_TROOPS 2018-04-07 14:21:34 +02:00
FlightControl_Master
a247f56c7e Progress on AI_CARGO_TROOPS 2018-04-07 10:18:52 +02:00
FlightControl_Master
718138abd6 Fixed slingload deploy problem.
Fixed messaging. Now a message is shown when cargo reports itself when in LoadRange and when near, it will allow for loading of cargo.
Fixed the perception that cargo can be boarded when loaded in an other carrier, which is totally wrong.
2018-04-06 21:33:54 +02:00
FlightControl_Master
269b52fd0e Fixed slingload deploy problem.
Fixed messaging. Now a message is shown when cargo reports itself when in LoadRange and when near, it will allow for loading of cargo.
Fixed the perception that cargo can be boarded when loaded in an other carrier, which is totally wrong.
2018-04-06 21:28:43 +02:00
funkyfranky
fd34bab65a Merge branch 'develop' into FF/Develop 2018-04-05 22:47:38 +02:00
funkyfranky
e2df7f9732 Merge branch 'master' into FF/Develop 2018-04-05 22:44:41 +02:00
funkyfranky
ed0c5b2264 Added Range 1.0.3
Just copied from other branch.
2018-04-05 22:44:33 +02:00
FlightControl_Master
2ebd25d677 Finish Cargo/FC/Csar 2018-04-05 19:54:24 +02:00
FlightControl_Master
376eeb63c5 Added CARGO_SLINGLOAD and fixed some bugs. 2018-04-05 19:44:04 +02:00
FlightControl_Master
5971f6de09 Added CARGO_SLINGLOAD 2018-04-05 19:43:24 +02:00
FlightControl_Master
a586d81f68 Cargo Crate transportation working :-) 2018-04-05 15:06:59 +02:00
FlightControl_Master
c60dda2545 Cargo Crate transportation working :-) 2018-04-05 15:06:36 +02:00
FlightControl_Master
45d1cb48bb Added Transport tasking to the dispatcher
# Conflicts:
#	Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua
2018-04-04 14:44:15 +02:00
FlightControl_Master
0d995d1832 Progress 2018-04-04 14:38:49 +02:00
funkyfranky
e094c8133a Added PseudoATC and Suppression 2018-04-03 16:49:34 +02:00
FlightControl_Master
1f578d4ab5 Working CSAR ... huray 2018-04-03 07:45:26 +02:00
FlightControl_Master
ec973fcc76 Working CSAR ... huray 2018-04-03 07:43:55 +02:00
FlightControl_Master
25831db057 Progress
-- Still need to fix an issue with some strang objects appearing and a crash.
2018-04-02 19:33:44 +02:00
FlightControl_Master
d0925ddfc1 Progress 2018-04-02 09:29:51 +02:00
FlightControl_Master
a4d3089fdb Updates Csar 2018-03-31 09:10:11 +02:00
FlightControl_Master
a94e744028 -- New modules for CSAR tasking. 2018-03-31 07:35:18 +02:00
FlightControl_Master
1401bb29aa New cargo files 2018-03-29 17:56:38 +02:00
FlightControl_Master
9688c606f0 New Cargo files 2018-03-29 17:55:58 +02:00
FlightControl_Master
c4ba2c0235 -- Fixing respawning of CargoGroups after dead event. 2018-03-29 12:48:47 +02:00
FlightControl_Master
35f18d0d1f -- Fixing respawning of CargoGroups after dead event. 2018-03-29 12:48:24 +02:00
FlightControl_Master
93640b1d8e -- Further fixes for TASK_CARGO_TRANSPORT 2018-03-29 12:02:07 +02:00
FlightControl_Master
75d179176d -- A lot of fixes to the new Cargo handling model. Now also TASK_CARGO_TRANSPORT is working. 2018-03-29 12:00:46 +02:00
FlightControl_Master
c37560275e -- A lot of fixes to the new Cargo handling model. Now also TASK_CARGO_TRANSPORT is working. 2018-03-29 12:00:11 +02:00
FlightControl_Master
812902b009 -- First prototype of AI_CARGO_TROOPS 2018-03-28 18:01:07 +02:00
FlightControl_Master
b29ce9b45e Boarding of troops 2018-03-28 18:00:17 +02:00
FlightControl_Master
31a7a4e993 Default of near radius is 25 meters. 2018-03-28 16:48:03 +02:00
FlightControl_Master
7a8881974c Optimizations of cargo deployment. 2018-03-28 16:42:37 +02:00
FlightControl_Master
ea069455d3 Merge branch 'AI_Cargo_Troops_old' into feature/AI_Cargo_Troops_Handling 2018-03-28 16:16:21 +02:00
FlightControl_Master
59bf7fdc96 Merged 'develop'.
# Conflicts:
#	Moose Development/Moose/Core/Cargo.lua
2018-03-28 13:49:57 +02:00
FlightControl_Master
2caada0119 -- Fixed a lot of issues with cargo when the cargo representative is destroyed. 2018-03-27 21:03:30 +02:00
FlightControl_Master
7a579a0ab5 -- Fixed a lot of issues with cargo when the cargo representative is destroyed. 2018-03-27 20:59:37 +02:00
FlightControl_Master
272308cf98 Finish SET_AIRBASE_does_not_update_#817 2018-03-27 15:10:54 +02:00
FlightControl_Master
b4b2034792 Finish SET_AIRBASE_does_not_update_#817 2018-03-27 15:03:47 +02:00
FlightControl_Master
12fcb99218 -- The infantry must only be 5 meters near. 2018-03-27 14:16:25 +02:00
FlightControl_Master
b1a1c6c552 -- The infantry must only be 5 meters near. 2018-03-27 14:16:04 +02:00
FlightControl_Master
61e2e8ca6b Added lua file 2018-03-27 13:57:09 +02:00
FlightControl_Master
21a7bac4e0 added file 2018-03-27 13:56:53 +02:00
FlightControl_Master
12c4e142e9 comment 2018-03-27 13:47:25 +02:00
FlightControl_Master
9759640d52 comment 2018-03-27 13:46:55 +02:00
FlightControl_Master
395923eb07 First prototype of AI_CARGO_TROOPS 2018-03-27 12:13:08 +02:00
FlightControl_Master
727d64927b First working prototype of AI_CARGO_TROOPS. 2018-03-27 12:07:16 +02:00
FlightControl_Master
b6fc46fdd0 Cargo Troops 2018-03-26 17:53:23 +02:00
FlightControl_Master
e7518d69e6 Cargo Troops 2018-03-26 07:39:55 +02:00
FlightControl_Master
cce90b1f46 New AI_CARGO_TROOPS class 2018-03-26 06:52:24 +02:00
FlightControl_Master
a15ef93e7c Merged 'develop'. 2018-03-26 06:10:11 +02:00
FlightControl_Master
7d1d165a5a Finish Detection_error_with_units_and_types 2018-03-26 05:34:20 +02:00
FlightControl_Master
c4f2446b92 fix 2018-03-25 08:17:33 +02:00
FlightControl_Master
7088c4c426 updated branched creation of include repo 2018-03-25 08:15:13 +02:00
FlightControl_Master
7963f04bdc progress 2018-03-24 08:01:49 +01:00
FlightControl_Master
ca32c57c52 Progress on the new cargo group movements 2018-03-24 07:32:04 +01:00
57 changed files with 14529 additions and 2863 deletions

View File

@@ -48,7 +48,7 @@ install:
build_script: build_script:
- ps: | - ps: |
if( $env:appveyor_repo_branch -eq 'master' ) if( $env:appveyor_repo_branch -eq 'master' -or $env:appveyor_repo_branch -eq 'develop' )
{ {
$apiUrl = 'https://ci.appveyor.com/api' $apiUrl = 'https://ci.appveyor.com/api'
$token = 'qts80b5kpq0ooj4x6vvw' $token = 'qts80b5kpq0ooj4x6vvw'
@@ -56,8 +56,8 @@ build_script:
"Authorization" = "Bearer $token" "Authorization" = "Bearer $token"
"Content-type" = "application/json" "Content-type" = "application/json"
} }
$RequestBody = @{ accountName = 'FlightControl-Master'; projectSlug = 'moose-include'; branch = 'master'; environmentVariables = @{} } | ConvertTo-Json $RequestBody = @{ accountName = 'FlightControl-Master'; projectSlug = 'moose-include'; branch = "$env:appveyor_repo_branch"; environmentVariables = @{} } | ConvertTo-Json
# get project with last build details # Generate the new version ...
$project = Invoke-RestMethod -method Post -Uri "$apiUrl/builds" -Headers $headers -Body $RequestBody $project = Invoke-RestMethod -method Post -Uri "$apiUrl/builds" -Headers $headers -Body $RequestBody
} }
- ps: | - ps: |

View File

@@ -0,0 +1,680 @@
--- **AI** -- (R2.3) - Models the intelligent transportation of infantry and other cargo.
--
-- ===
--
-- ### Author: **FlightControl**
--
-- ===
--
-- @module AI_Cargo_APC
--- @type AI_CARGO_APC
-- @extends Core.Fsm#FSM_CONTROLLABLE
--- # AI\_CARGO\_APC class, extends @{Core.Base#BASE}
--
-- ===
--
-- AI\_CARGO\APC brings a dynamic cargo handling capability for AI groups.
--
-- 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 module uses the @{Cargo} capabilities within the MOOSE framework.
-- CARGO derived objects must be declared within the mission to make the AI\_CARGO\APC object recognize the cargo.
-- Please consult the @{Cargo} module for more information.
--
-- ## Cargo loading.
--
-- The module will load automatically cargo when the APCs are within boarding or loading range.
-- The boarding or loading range is specified when the cargo is created in the simulation, and therefore, this range depends on the type of cargo
-- and the specified boarding range.
--
-- ## Enemies nearby.
--
-- 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 range needs to be specified in meters at the @{#AI_CARGO_APC.New}() method.
-- This combat range will trigger the unboarding of troops when enemies are within the combat range around the APCs.
-- During my tests, I've noticed that there is a balance between ensuring that the infantry is within sufficient hit range (effectiveness) versus
-- vulnerability of the infantry. It all depends on the kind of enemies that are expected to be encountered.
-- A combat range of 350 meters to 500 meters has been proven to be the most effective and efficient.
--
-- ## 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 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.
--
--
--
-- @field #AI_CARGO_APC
AI_CARGO_APC = {
ClassName = "AI_CARGO_APC",
Coordinate = nil, -- Core.Point#COORDINATE,
APC_Cargo = {},
}
--- Creates a new AI_CARGO_APC object.
-- @param #AI_CARGO_APC self
-- @param Wrapper.Group#GROUP CargoCarrier
-- @param Core.Set#SET_CARGO CargoSet
-- @param #number CombatRadius
-- @return #AI_CARGO_APC
function AI_CARGO_APC:New( CargoCarrier, CargoSet, CombatRadius )
local self = BASE:Inherit( self, FSM_CONTROLLABLE:New() ) -- #AI_CARGO_APC
self.CargoSet = CargoSet -- Core.Set#SET_CARGO
self.CombatRadius = CombatRadius
self:SetStartState( "Unloaded" )
self:AddTransition( "Unloaded", "Pickup", "*" )
self:AddTransition( "Loaded", "Deploy", "*" )
self:AddTransition( "*", "Load", "Boarding" )
self:AddTransition( "Boarding", "Board", "Boarding" )
self:AddTransition( "Boarding", "Loaded", "Loaded" )
self:AddTransition( "Loaded", "Unload", "Unboarding" )
self:AddTransition( "Unboarding", "Unboard", "Unboarding" )
self:AddTransition( { "Unboarding", "Unloaded" }, "Unloaded", "Unloaded" )
self:AddTransition( "*", "Monitor", "*" )
self:AddTransition( "*", "Follow", "Following" )
self:AddTransition( "*", "Guard", "Unloaded" )
self:AddTransition( "*", "Home", "*" )
self:AddTransition( "*", "Destroyed", "Destroyed" )
--- Pickup Handler OnBefore for AI_CARGO_APC
-- @function [parent=#AI_CARGO_APC] OnBeforePickup
-- @param #AI_CARGO_APC self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param Core.Point#COORDINATE Coordinate
-- @return #boolean
--- Pickup Handler OnAfter for AI_CARGO_APC
-- @function [parent=#AI_CARGO_APC] OnAfterPickup
-- @param #AI_CARGO_APC self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param Core.Point#COORDINATE Coordinate
--- Pickup Trigger for AI_CARGO_APC
-- @function [parent=#AI_CARGO_APC] Pickup
-- @param #AI_CARGO_APC self
-- @param Core.Point#COORDINATE Coordinate
--- Pickup Asynchronous Trigger for AI_CARGO_APC
-- @function [parent=#AI_CARGO_APC] __Pickup
-- @param #AI_CARGO_APC self
-- @param #number Delay
-- @param Core.Point#COORDINATE Coordinate
--- Deploy Handler OnBefore for AI_CARGO_APC
-- @function [parent=#AI_CARGO_APC] OnBeforeDeploy
-- @param #AI_CARGO_APC self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param Core.Point#COORDINATE Coordinate
-- @return #boolean
--- Deploy Handler OnAfter for AI_CARGO_APC
-- @function [parent=#AI_CARGO_APC] OnAfterDeploy
-- @param #AI_CARGO_APC self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param Core.Point#COORDINATE Coordinate
--- Deploy Trigger for AI_CARGO_APC
-- @function [parent=#AI_CARGO_APC] Deploy
-- @param #AI_CARGO_APC self
-- @param Core.Point#COORDINATE Coordinate
--- Deploy Asynchronous Trigger for AI_CARGO_APC
-- @function [parent=#AI_CARGO_APC] __Deploy
-- @param #AI_CARGO_APC self
-- @param Core.Point#COORDINATE Coordinate
-- @param #number Delay
--- Loaded Handler OnAfter for AI_CARGO_APC
-- @function [parent=#AI_CARGO_APC] OnAfterLoaded
-- @param #AI_CARGO_APC self
-- @param Wrapper.Group#GROUP APC
-- @param #string From
-- @param #string Event
-- @param #string To
--- Unloaded Handler OnAfter for AI_CARGO_APC
-- @function [parent=#AI_CARGO_APC] OnAfterUnloaded
-- @param #AI_CARGO_APC self
-- @param Wrapper.Group#GROUP APC
-- @param #string From
-- @param #string Event
-- @param #string To
self:__Monitor( 1 )
self:SetCarrier( CargoCarrier )
self.Transporting = false
self.Relocating = false
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 )
CargoCarrier:HandleEvent( EVENTS.Hit )
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 range. Unload the CargoCarrier.
AICargoTroops:Destroyed()
end
end
end
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 range. 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
function AI_CARGO_APC:IsTransporting()
return self.Transporting == true
end
function AI_CARGO_APC:IsRelocating()
return self.Relocating == true
end
--- Find a free Carrier within a range.
-- @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
--- 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
--- @param #AI_CARGO_APC self
-- @param Wrapper.Group#GROUP APC
function AI_CARGO_APC:onafterMonitor( APC, From, Event, To )
self:F( { APC, From, Event, To } )
if APC and APC:IsAlive() then
if self.CarrierCoordinate then
if self:IsRelocating() == true then
local Coordinate = APC:GetCoordinate()
self.Zone:Scan( { Object.Category.UNIT } )
if self.Zone:IsAllInZoneOfCoalition( self.Coalition ) then
if self:Is( "Unloaded" ) or self:Is( "Following" ) then
-- There are no enemies within combat range. Load the CargoCarrier.
self:Load()
end
else
if self:Is( "Loaded" ) then
-- There are enemies within combat range. Unload the CargoCarrier.
self:__Unload( 1, false )
else
if self:Is( "Unloaded" ) then
self:Follow()
end
if self:Is( "Following" ) then
for APCUnit, Cargo in pairs( self.APC_Cargo ) do
local Cargo = Cargo -- Cargo.Cargo#CARGO
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
self.CarrierCoordinate = APC:GetCoordinate()
end
self:__Monitor( -5 )
end
--- @param #AI_CARGO_APC self
-- @param Wrapper.Group#GROUP APC
function AI_CARGO_APC:onbeforeLoad( APC, From, Event, To )
self:F( { APC, From, Event, To } )
local Boarding = false
self.BoardingCount = 0
if APC and APC:IsAlive() then
self.APC_Cargo = {}
for _, APCUnit in pairs( APC:GetUnits() ) do
local APCUnit = APCUnit -- Wrapper.Unit#UNIT
for _, Cargo in pairs( self.CargoSet:GetSet() ) do
local Cargo = Cargo -- Cargo.Cargo#CARGO
self:F( { IsUnLoaded = Cargo:IsUnLoaded(), IsDeployed = Cargo:IsDeployed(), Cargo:GetName(), APC:GetName() } )
if Cargo:IsUnLoaded() and not Cargo:IsDeployed() then
if Cargo:IsInLoadRadius( APCUnit:GetCoordinate() ) then
self:F( { "In radius", APCUnit:GetName() } )
APC:RouteStop()
--Cargo:Ungroup()
Cargo:Board( APCUnit, 25 )
self:__Board( 1, Cargo )
Boarding = true
-- So now this APCUnit has Cargo that is being loaded.
-- This will be used further in the logic to follow and to check cargo status.
self.APC_Cargo[APCUnit] = Cargo
break
end
end
end
end
end
return Boarding
end
--- @param #AI_CARGO_APC self
-- @param Wrapper.Group#GROUP APC
function AI_CARGO_APC:onafterBoard( APC, From, Event, To, Cargo )
self:F( { APC, From, Event, To, Cargo } )
if APC and APC:IsAlive() then
self:F({ IsLoaded = Cargo:IsLoaded(), Cargo:GetName(), APC:GetName() } )
if not Cargo:IsLoaded() then
self:__Board( 10, Cargo )
else
self:__Loaded( 1 )
end
end
end
--- @param #AI_CARGO_APC self
-- @param Wrapper.Group#GROUP APC
function AI_CARGO_APC:onbeforeLoaded( APC, From, Event, To )
self:F( { APC, From, Event, To } )
local Loaded = true
if APC and APC:IsAlive() then
for APCUnit, Cargo in pairs( self.APC_Cargo ) do
local Cargo = Cargo -- Cargo.Cargo#CARGO
self:F( { IsLoaded = Cargo:IsLoaded(), IsDestroyed = Cargo:IsDestroyed(), Cargo:GetName(), APC:GetName() } )
if not Cargo:IsLoaded() and not Cargo:IsDestroyed() then
Loaded = false
end
end
end
if Loaded == true then
APC:RouteResume()
end
return Loaded
end
--- @param #AI_CARGO_APC self
-- @param Wrapper.Group#GROUP APC
function AI_CARGO_APC:onafterUnload( APC, From, Event, To, Deployed )
self:F( { APC, From, Event, To, Deployed } )
if APC and APC:IsAlive() then
for _, APCUnit in pairs( APC:GetUnits() ) do
local APCUnit = APCUnit -- Wrapper.Unit#UNIT
APC:RouteStop()
for _, Cargo in pairs( APCUnit:GetCargo() ) do
Cargo:UnBoard()
self:__Unboard( 10, Cargo, Deployed )
end
end
end
end
--- @param #AI_CARGO_APC self
-- @param Wrapper.Group#GROUP APC
function AI_CARGO_APC:onafterUnboard( APC, From, Event, To, Cargo, Deployed )
self:F( { APC, From, Event, To, Cargo:GetName() } )
if APC and APC:IsAlive() then
if not Cargo:IsUnLoaded() then
self:__Unboard( 10, Cargo, Deployed )
else
self:__Unloaded( 1, Cargo, Deployed )
end
end
end
--- @param #AI_CARGO_APC self
-- @param Wrapper.Group#GROUP APC
function AI_CARGO_APC:onbeforeUnloaded( APC, From, Event, To, Cargo, Deployed )
self:F( { APC, From, Event, To, Cargo:GetName(), Deployed = Deployed } )
local AllUnloaded = true
--Cargo:Regroup()
if APC and APC:IsAlive() then
for _, APCUnit in pairs( APC:GetUnits() ) do
local APCUnit = APCUnit -- Wrapper.Unit#UNIT
local CargoCheck = self.APC_Cargo[APCUnit]
if CargoCheck then
self:F( { CargoCheck:GetName(), IsUnLoaded = CargoCheck:IsUnLoaded() } )
if CargoCheck:IsUnLoaded() == false then
AllUnloaded = false
break
end
end
end
if AllUnloaded == true then
if Deployed == true then
for APCUnit, Cargo in pairs( self.APC_Cargo ) do
local Cargo = Cargo -- Cargo.Cargo#CARGO
Cargo:SetDeployed( true )
end
self.APC_Cargo = {}
end
self:Guard()
self.CargoCarrier = APC
end
end
self:F( { AllUnloaded = AllUnloaded } )
return AllUnloaded
end
--- @param #AI_CARGO_APC self
-- @param Wrapper.Group#GROUP APC
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 APCUnit, Cargo in pairs( self.APC_Cargo ) do
local Cargo = Cargo -- Cargo.Cargo#CARGO
if Cargo:IsUnLoaded() then
self:FollowToCarrier( self, APCUnit, Cargo )
APCUnit:RouteResume()
end
end
end
end
--- @param #AI_CARGO_APC
-- @param Wrapper.Group#GROUP APC
function AI_CARGO_APC._Pickup( APC, self )
APC:F( { "AI_CARGO_APC._Pickup:", APC:GetName() } )
if APC:IsAlive() then
self:Load()
self.Relocating = true
end
end
--- @param #AI_CARGO_APC
-- @param Wrapper.Group#GROUP APC
function AI_CARGO_APC._Deploy( APC, self )
APC:F( { "AI_CARGO_APC._Deploy:", APC } )
if APC:IsAlive() then
self:Unload( true )
self.Transporting = false
self.Relocating = false
end
end
--- @param #AI_CARGO_APC self
-- @param Wrapper.Group#GROUP APC
-- @param From
-- @param Event
-- @param To
-- @param Core.Point#COORDINATE Coordinate
-- @param #number Speed
-- @param #string EndPointFormation The formation at the end point of the action.
function AI_CARGO_APC:onafterPickup( APC, From, Event, To, Coordinate )
if APC and APC:IsAlive() then
if Coordinate then
self.RoutePickup = true
local Waypoints = APC:TaskGroundOnRoad( Coordinate, 150, "Line abreast" )
local TaskFunction = APC:TaskFunction( "AI_CARGO_APC._Pickup", self )
self:F({Waypoints = Waypoints})
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 )
end
self.Transporting = true
end
end
--- @param #AI_CARGO_APC self
-- @param Wrapper.Group#GROUP APC
-- @param From
-- @param Event
-- @param To
-- @param Core.Point#COORDINATE Coordinate
-- @param #number Speed
-- @param #string EndPointFormation The formation at the end point of the action.
function AI_CARGO_APC:onafterDeploy( APC, From, Event, To, Coordinate )
if APC and APC:IsAlive() then
self.RouteDeploy = true
local Waypoints = APC:TaskGroundOnRoad( Coordinate, 150, "Line abreast" )
local TaskFunction = APC:TaskFunction( "AI_CARGO_APC._Deploy", self )
self:F({Waypoints = Waypoints})
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.
end
end
--- @param #AI_CARGO_HELICOPTER self
-- @param Wrapper.Group#GROUP APC
-- @param From
-- @param Event
-- @param To
-- @param Core.Point#COORDINATE Coordinate
-- @param #number Speed
function AI_CARGO_APC:onafterHome( APC, From, Event, To, Coordinate )
if APC and APC:IsAlive() ~= nil then
self.RouteHome = true
local Waypoints = APC:TaskGroundOnRoad( Coordinate, 120, "Line abreast" )
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

@@ -0,0 +1,448 @@
--- **AI** -- (R2.3) - Models the intelligent transportation of infantry (cargo).
--
-- ===
--
-- ### Author: **FlightControl**
--
-- ===
--
-- @module AI_Cargo_Airplane
--- @type AI_CARGO_AIRPLANE
-- @extends Core.Fsm#FSM_CONTROLLABLE
--- # AI\_CARGO\_AIRPLANE class, extends @{Core.Base@BASE}
--
-- ===
--
-- @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
-- @param Core.Set#SET_CARGO CargoSet
-- @param #number CombatRadius
-- @return #AI_CARGO_AIRPLANE
function AI_CARGO_AIRPLANE:New( Airplane, CargoSet )
local self = BASE:Inherit( self, FSM_CONTROLLABLE:New() ) -- #AI_CARGO_AIRPLANE
self.CargoSet = CargoSet -- Cargo.CargoGroup#CARGO_GROUP
self:SetStartState( "Unloaded" )
self:AddTransition( "Unloaded", "Pickup", "*" )
self:AddTransition( "Loaded", "Deploy", "*" )
self:AddTransition( "Unloaded", "Load", "Boarding" )
self:AddTransition( "Boarding", "Board", "Boarding" )
self:AddTransition( "Boarding", "Loaded", "Loaded" )
self:AddTransition( "Loaded", "Unload", "Unboarding" )
self:AddTransition( "Unboarding", "Unboard", "Unboarding" )
self:AddTransition( "Unboarding", "Unloaded", "Unloaded" )
self:AddTransition( "*", "Landed", "*" )
self:AddTransition( "*", "Destroyed", "Destroyed" )
--- Pickup Handler OnBefore for AI_CARGO_AIRPLANE
-- @function [parent=#AI_CARGO_AIRPLANE] OnBeforePickup
-- @param #AI_CARGO_AIRPLANE self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param Wrapper.Airbase#AIRBASE Airbase
-- @return #boolean
--- Pickup Handler OnAfter for AI_CARGO_AIRPLANE
-- @function [parent=#AI_CARGO_AIRPLANE] OnAfterPickup
-- @param #AI_CARGO_AIRPLANE self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param Wrapper.Airbase#AIRBASE Airbase
--- Pickup Trigger for AI_CARGO_AIRPLANE
-- @function [parent=#AI_CARGO_AIRPLANE] Pickup
-- @param #AI_CARGO_AIRPLANE self
-- @param Wrapper.Airbase#AIRBASE Airbase
--- Pickup Asynchronous Trigger for AI_CARGO_AIRPLANE
-- @function [parent=#AI_CARGO_AIRPLANE] __Pickup
-- @param #AI_CARGO_AIRPLANE self
-- @param #number Delay
-- @param Wrapper.Airbase#AIRBASE Airbase
--- Deploy Handler OnBefore for AI_CARGO_AIRPLANE
-- @function [parent=#AI_CARGO_AIRPLANE] OnBeforeDeploy
-- @param #AI_CARGO_AIRPLANE self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param Wrapper.Airbase#AIRBASE Airbase
-- @return #boolean
--- Deploy Handler OnAfter for AI_CARGO_AIRPLANE
-- @function [parent=#AI_CARGO_AIRPLANE] OnAfterDeploy
-- @param #AI_CARGO_AIRPLANE self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param Wrapper.Airbase#AIRBASE Airbase
--- Deploy Trigger for AI_CARGO_AIRPLANE
-- @function [parent=#AI_CARGO_AIRPLANE] Deploy
-- @param #AI_CARGO_AIRPLANE self
-- @param Wrapper.Airbase#AIRBASE Airbase
--- Deploy Asynchronous Trigger for AI_CARGO_AIRPLANE
-- @function [parent=#AI_CARGO_AIRPLANE] __Deploy
-- @param #AI_CARGO_AIRPLANE self
-- @param Wrapper.Airbase#AIRBASE Airbase
-- @param #number Delay
self:SetCarrier( Airplane )
return self
end
--- Set the Carrier.
-- @param #AI_CARGO_AIRPLANE self
-- @param Wrapper.Group#GROUP Airplane
-- @return #AI_CARGO_AIRPLANE
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:Landed()
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
--- @param #AI_CARGO_AIRPLANE self
-- @param Wrapper.Group#GROUP Airplane
-- @param From
-- @param Event
-- @param To
-- @param Wrapper.Airbase#AIRBASE Airbase
-- @param #number Speed
function AI_CARGO_AIRPLANE:onafterLanded( Airplane, From, Event, To )
if Airplane and Airplane:IsAlive() then
if self.RoutePickup == true then
self:Load( Airplane:GetPointVec2() )
self.RoutePickup = false
end
if self.RouteDeploy == true then
self:Unload()
self.RouteDeploy = false
end
end
end
--- @param #AI_CARGO_AIRPLANE self
-- @param Wrapper.Group#GROUP Airplane
-- @param From
-- @param Event
-- @param To
-- @param Wrapper.Airbase#AIRBASE Airbase
-- @param #number Speed
function AI_CARGO_AIRPLANE:onafterPickup( Airplane, From, Event, To, Airbase, Speed )
if Airplane and Airplane:IsAlive() then
self:Route( Airplane, Airbase, Speed )
self.RoutePickup = true
self.Airbase = Airbase
end
end
--- @param #AI_CARGO_AIRPLANE self
-- @param Wrapper.Group#GROUP Airplane
-- @param From
-- @param Event
-- @param To
-- @param Wrapper.Airbase#AIRBASE Airbase
-- @param #number Speed
function AI_CARGO_AIRPLANE:onafterDeploy( Airplane, From, Event, To, Airbase, Speed )
if Airplane and Airplane:IsAlive() then
self:Route( Airplane, Airbase, Speed )
self.RouteDeploy = true
self.Airbase = Airbase
end
end
--- @param #AI_CARGO_AIRPLANE self
-- @param Wrapper.Group#GROUP Airplane
function AI_CARGO_AIRPLANE:onafterLoad( Airplane, From, Event, To, Coordinate )
if Airplane and Airplane:IsAlive() then
for _, Cargo in pairs( self.CargoSet:GetSet() ) do
if Cargo:IsInLoadRadius( Coordinate ) then
self:__Board( 5 )
Cargo:Board( Airplane, 25 )
self.Cargo = Cargo
break
end
end
end
end
--- @param #AI_CARGO_AIRPLANE self
-- @param Wrapper.Group#GROUP Airplane
function AI_CARGO_AIRPLANE:onafterBoard( Airplane, From, Event, To )
if Airplane and Airplane:IsAlive() then
self:F({ IsLoaded = self.Cargo:IsLoaded() } )
if not self.Cargo:IsLoaded() then
self:__Board( 10 )
else
self:__Loaded( 1 )
end
end
end
--- @param #AI_CARGO_AIRPLANE self
-- @param Wrapper.Group#GROUP Airplane
function AI_CARGO_AIRPLANE:onafterLoaded( Airplane, From, Event, To )
if Airplane and Airplane:IsAlive() then
end
end
--- @param #AI_CARGO_AIRPLANE self
-- @param Wrapper.Group#GROUP Airplane
function AI_CARGO_AIRPLANE:onafterUnload( Airplane, From, Event, To )
if Airplane and Airplane:IsAlive() then
self.Cargo:UnBoard()
self:__Unboard( 10 )
end
end
--- @param #AI_CARGO_AIRPLANE self
-- @param Wrapper.Group#GROUP Airplane
function AI_CARGO_AIRPLANE:onafterUnboard( Airplane, From, Event, To )
if Airplane and Airplane:IsAlive() then
if not self.Cargo:IsUnLoaded() then
self:__Unboard( 10 )
else
self:__Unloaded( 1 )
end
end
end
--- @param #AI_CARGO_AIRPLANE self
-- @param Wrapper.Group#GROUP Airplane
function AI_CARGO_AIRPLANE:onafterUnloaded( Airplane, From, Event, To )
if Airplane and Airplane:IsAlive() then
self.Airplane = Airplane
end
end
--- @param #AI_CARGO_AIRPLANE self
-- @param Wrapper.Group#GROUP Airplane
-- @param Wrapper.Airbase#AIRBASE Airbase
-- @param #number Speed
function AI_CARGO_AIRPLANE:Route( Airplane, Airbase, Speed )
if Airplane and Airplane:IsAlive() then
local PointVec3 = Airplane:GetPointVec3()
local Takeoff = SPAWN.Takeoff.Hot
local Template = Airplane:GetTemplate()
if Template then
local Points = {}
if self.Airbase then
local FromWaypoint = Template.route.points[1]
-- These are only for ships.
FromWaypoint.linkUnit = nil
FromWaypoint.helipadId = nil
FromWaypoint.airdromeId = nil
local AirbaseID = self.Airbase:GetID()
local AirbaseCategory = self.Airbase:GetDesc().category
FromWaypoint.airdromeId = AirbaseID
FromWaypoint.alt = 0
FromWaypoint.type = GROUPTEMPLATE.Takeoff[Takeoff][1] -- type
FromWaypoint.action = GROUPTEMPLATE.Takeoff[Takeoff][2] -- action
-- Translate the position of the Group Template to the Vec3.
for UnitID = 1, #Template.units do
self:T( 'Before Translation SpawnTemplate.units['..UnitID..'].x = ' .. Template.units[UnitID].x .. ', SpawnTemplate.units['..UnitID..'].y = ' .. Template.units[UnitID].y )
-- These cause a lot of confusion.
local UnitTemplate = Template.units[UnitID]
UnitTemplate.parking = 15
UnitTemplate.parking_id = "1"
UnitTemplate.alt = 0
local SX = UnitTemplate.x
local SY = UnitTemplate.y
local BX = FromWaypoint.x
local BY = FromWaypoint.y
local TX = PointVec3.x + ( SX - BX )
local TY = PointVec3.z + ( SY - BY )
UnitTemplate.x = TX
UnitTemplate.y = TY
self:T( 'After Translation SpawnTemplate.units['..UnitID..'].x = ' .. UnitTemplate.x .. ', SpawnTemplate.units['..UnitID..'].y = ' .. UnitTemplate.y )
end
FromWaypoint.x = PointVec3.x
FromWaypoint.y = PointVec3.z
Points[#Points+1] = FromWaypoint
else
local GroupPoint = Airplane:GetVec2()
local GroupVelocity = Airplane:GetUnit(1):GetDesc().speedMax
local FromWaypoint = {}
FromWaypoint.x = GroupPoint.x
FromWaypoint.y = GroupPoint.y
FromWaypoint.type = "Turning Point"
FromWaypoint.action = "Turning Point"
FromWaypoint.speed = GroupVelocity
Points[#Points+1] = FromWaypoint
end
local AirbasePointVec2 = Airbase:GetPointVec2()
local ToWaypoint = AirbasePointVec2:WaypointAir(
POINT_VEC3.RoutePointAltType.BARO,
"Land",
"Landing",
Speed or Airplane:GetUnit(1):GetDesc().speedMax
)
ToWaypoint["airdromeId"] = Airbase:GetID()
ToWaypoint["speed_locked"] = true,
self:F( ToWaypoint )
Points[#Points+1] = ToWaypoint
Template.x = PointVec3.x
Template.y = PointVec3.z
self:T3( Points )
Template.route.points = Points
--self:Respawn( Template )
local GroupSpawned = Airplane:Respawn( Template )
return GroupSpawned
end
end
end

View File

@@ -0,0 +1,356 @@
--- **AI** -- (R2.4) - Models the intelligent transportation of infantry and other cargo.
--
-- ===
--
-- ### Author: **FlightControl**
--
-- ===
--
-- @module AI_Cargo_Dispatcher
--- @type AI_CARGO_DISPATCHER
-- @extends Core.Fsm#FSM_CONTROLLABLE
--- # AI\_CARGO\_DISPATCHER class, extends @{Core.Base#BASE}
--
-- ===
--
-- AI\_CARGO\_DISPATCHER brings a dynamic cargo handling 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 module uses the @{Cargo} capabilities within the MOOSE framework.
-- CARGO derived objects must be declared within the mission to make the AI\_CARGO\_DISPATCHER object recognize the cargo.
-- Please consult the @{Cargo} module for more information.
--
--
--
-- @field #AI_CARGO_DISPATCHER
AI_CARGO_DISPATCHER = {
ClassName = "AI_CARGO_DISPATCHER",
SetAPC = nil,
SetDeployZones = nil,
AI_CARGO_APC = {}
}
--- @type AI_CARGO_DISPATCHER.AI_CARGO_APC
-- @map <Wrapper.Group#GROUP, AI.AI_Cargo_APC#AI_CARGO_APC>
--- @field #AI_CARGO_DISPATCHER.AI_CARGO_APC
AI_CARGO_DISPATCHER.AI_Cargo = {}
--- @field #AI_CARGO_DISPATCHER.PickupCargo
AI_CARGO_DISPATCHER.PickupCargo = {}
--- Creates a new AI_CARGO_DISPATCHER object.
-- @param #AI_CARGO_DISPATCHER self
-- @param Core.Set#SET_GROUP SetAPC
-- @param Core.Set#SET_CARGO SetCargo
-- @param Core.Set#SET_ZONE SetDeployZone
-- @return #AI_CARGO_DISPATCHER
-- @usage
--
-- -- Create a new cargo dispatcher
-- SetAPC = SET_GROUP:New():FilterPrefixes( "APC" ):FilterStart()
-- SetCargo = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart()
-- SetDeployZone = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart()
-- AICargoDispatcher = AI_CARGO_DISPATCHER:New( SetAPC, SetCargo, SetDeployZone )
--
function AI_CARGO_DISPATCHER:New( SetAPC, SetCargo, SetDeployZones )
local self = BASE:Inherit( self, FSM:New() ) -- #AI_CARGO_DISPATCHER
self.SetAPC = SetAPC -- Core.Set#SET_GROUP
self.SetCargo = SetCargo -- Core.Set#SET_CARGO
self.SetDeployZones = SetDeployZones -- Core.Set#SET_ZONE
self:SetStartState( "Dispatch" )
self:AddTransition( "*", "Monitor", "*" )
self:AddTransition( "*", "Pickup", "*" )
self:AddTransition( "*", "Loading", "*" )
self:AddTransition( "*", "Loaded", "*" )
self:AddTransition( "*", "Deploy", "*" )
self:AddTransition( "*", "Unloading", "*" )
self:AddTransition( "*", "Unloaded", "*" )
self:AddTransition( "*", "Home", "*" )
self.MonitorTimeInterval = 30
self.DeployRadiusInner = 200
self.DeployRadiusOuter = 500
self.CarrierHome = {}
return self
end
--- Set the home zone.
-- When there is nothing anymore to pickup, the carriers will go to a random coordinate in this zone.
-- They will await here new orders.
-- @param #AI_CARGO_DISPATCHER self
-- @param Core.Zone#ZONE_BASE HomeZone
-- @return #AI_CARGO_DISPATCHER
-- @usage
--
-- -- Create a new cargo dispatcher
-- AICargoDispatcher = AI_CARGO_DISPATCHER:New( SetAPC, SetCargo, SetDeployZone )
--
-- -- Set the home coordinate
-- local HomeZone = ZONE:New( "Home" )
-- AICargoDispatcher:SetHomeZone( HomeZone )
--
function AI_CARGO_DISPATCHER:SetHomeZone( HomeZone )
self.HomeZone = HomeZone
return self
end
--- Sets or randomizes the pickup location for the carrier around the cargo coordinate in a radius defined an outer and optional inner radius.
-- This radius is influencing the location where the carrier will land to pickup the cargo.
-- There are two aspects that are very important to remember and take into account:
--
-- - Ensure that the outer and inner radius are within reporting radius set by the cargo.
-- For example, if the cargo has a reporting radius of 400 meters, and the outer and inner radius is set to 500 and 450 respectively,
-- then no cargo will be loaded!!!
-- - Also take care of the potential cargo position and possible reasons to crash the carrier. This is especially important
-- for locations which are crowded with other objects, like in the middle of villages or cities.
-- So, for the best operation of cargo operations, always ensure that the cargo is located at open spaces.
--
-- The default radius is 0, so the center. In case of a polygon zone, a random location will be selected as the center in the zone.
-- @param #AI_CARGO_DISPATCHER self
-- @param #number OuterRadius The outer radius in meters around the cargo coordinate.
-- @param #number InnerRadius (optional) The inner radius in meters around the cargo coordinate.
-- @return #AI_CARGO_DISPATCHER
-- @usage
--
-- -- Create a new cargo dispatcher
-- AICargoDispatcher = AI_CARGO_DISPATCHER:New( SetAPC, SetCargo, SetDeployZone )
--
-- -- Set the carrier to land within a band around the cargo coordinate between 500 and 300 meters!
-- AICargoDispatcher:SetPickupRadius( 500, 300 )
--
function AI_CARGO_DISPATCHER:SetPickupRadius( OuterRadius, InnerRadius )
OuterRadius = OuterRadius or 0
InnerRadius = InnerRadius or OuterRadius
self.PickupOuterRadius = OuterRadius
self.PickupInnerRadius = InnerRadius
return self
end
--- Set the speed or randomizes the speed in km/h to pickup the cargo.
-- @param #AI_CARGO_DISPATCHER self
-- @param #number MaxSpeed (optional) The maximum speed to move to the cargo pickup location.
-- @param #number MinSpeed The minimum speed to move to the cargo pickup location.
-- @return #AI_CARGO_DISPATCHER
-- @usage
--
-- -- Create a new cargo dispatcher
-- AICargoDispatcher = AI_CARGO_DISPATCHER:New( SetAPC, SetCargo, SetDeployZone )
--
-- -- Set the minimum pickup speed to be 100 km/h and the maximum speed to be 200 km/h.
-- AICargoDispatcher:SetPickupSpeed( 200, 100 )
--
function AI_CARGO_DISPATCHER:SetPickupSpeed( MaxSpeed, MinSpeed )
MaxSpeed = MaxSpeed or 999
MinSpeed = MinSpeed or MaxSpeed
self.PickupMinSpeed = MinSpeed
self.PickupMaxSpeed = MaxSpeed
return self
end
--- Sets or randomizes the deploy location for the carrier around the cargo coordinate in a radius defined an outer and an optional inner radius.
-- This radius is influencing the location where the carrier will land to deploy the cargo.
-- There is an aspect that is very important to remember and take into account:
--
-- - Take care of the potential cargo position and possible reasons to crash the carrier. This is especially important
-- for locations which are crowded with other objects, like in the middle of villages or cities.
-- So, for the best operation of cargo operations, always ensure that the cargo is located at open spaces.
--
-- The default radius is 0, so the center. In case of a polygon zone, a random location will be selected as the center in the zone.
-- @param #AI_CARGO_DISPATCHER self
-- @param #number OuterRadius The outer radius in meters around the cargo coordinate.
-- @param #number InnerRadius (optional) The inner radius in meters around the cargo coordinate.
-- @return #AI_CARGO_DISPATCHER
-- @usage
--
-- -- Create a new cargo dispatcher
-- AICargoDispatcher = AI_CARGO_DISPATCHER:New( SetAPC, SetCargo, SetDeployZone )
--
-- -- Set the carrier to land within a band around the cargo coordinate between 500 and 300 meters!
-- AICargoDispatcher:SetDeployRadius( 500, 300 )
--
function AI_CARGO_DISPATCHER:SetDeployRadius( OuterRadius, InnerRadius )
OuterRadius = OuterRadius or 0
InnerRadius = InnerRadius or OuterRadius
self.DeployOuterRadius = OuterRadius
self.DeployInnerRadius = InnerRadius
return self
end
--- Sets or randomizes the speed in km/h to deploy the cargo.
-- @param #AI_CARGO_DISPATCHER self
-- @param #number MaxSpeed The maximum speed to move to the cargo deploy location.
-- @param #number MinSpeed (optional) The minimum speed to move to the cargo deploy location.
-- @return #AI_CARGO_DISPATCHER
-- @usage
--
-- -- Create a new cargo dispatcher
-- AICargoDispatcher = AI_CARGO_DISPATCHER:New( SetAPC, SetCargo, SetDeployZone )
--
-- -- Set the minimum deploy speed to be 100 km/h and the maximum speed to be 200 km/h.
-- AICargoDispatcher:SetDeploySpeed( 200, 100 )
--
function AI_CARGO_DISPATCHER:SetDeploySpeed( MaxSpeed, MinSpeed )
MaxSpeed = MaxSpeed or 999
MinSpeed = MinSpeed or MaxSpeed
self.DeployMinSpeed = MinSpeed
self.DeployMaxSpeed = MaxSpeed
return self
end
--- The Start trigger event, which actually takes action at the specified time interval.
-- @param #AI_CARGO_DISPATCHER self
-- @param Wrapper.Group#GROUP APC
-- @return #AI_CARGO_DISPATCHER
function AI_CARGO_DISPATCHER:onafterMonitor()
for APCGroupName, Carrier in pairs( self.SetAPC:GetSet() ) do
local Carrier = Carrier -- Wrapper.Group#GROUP
local AI_Cargo = self.AI_Cargo[Carrier]
if not AI_Cargo then
-- ok, so this APC does not have yet an AI_CARGO_APC object...
-- let's create one and also declare the Loaded and UnLoaded handlers.
self.AI_Cargo[Carrier] = self:AICargo( Carrier, self.SetCargo, self.CombatRadius )
AI_Cargo = self.AI_Cargo[Carrier]
function AI_Cargo.OnAfterPickup( AI_Cargo, APC, From, Event, To, Cargo )
self:Pickup( APC, Cargo )
end
function AI_Cargo.OnAfterLoad( AI_Cargo, APC )
self:Loading( APC )
end
function AI_Cargo.OnAfterLoaded( AI_Cargo, APC, From, Event, To, Cargo )
self:Loaded( APC, Cargo )
end
function AI_Cargo.OnAfterDeploy( AI_Cargo, APC )
self:Deploy( APC )
end
function AI_Cargo.OnAfterUnload( AI_Cargo, APC )
self:Unloading( APC )
end
function AI_Cargo.OnAfterUnloaded( AI_Cargo, APC )
self:Unloaded( APC )
end
end
-- The Pickup sequence ...
-- Check if this APC need to go and Pickup something...
self:I( { IsTransporting = AI_Cargo:IsTransporting() } )
if AI_Cargo:IsTransporting() == false then
-- ok, so there is a free APC
-- now find the first cargo that is Unloaded
local PickupCargo = nil
for CargoName, Cargo in pairs( self.SetCargo:GetSet() ) do
local Cargo = Cargo -- Cargo.Cargo#CARGO
self:F( { Cargo = Cargo:GetName(), UnLoaded = Cargo:IsUnLoaded(), Deployed = Cargo:IsDeployed(), PickupCargo = self.PickupCargo[Cargo] ~= nil } )
if Cargo:IsUnLoaded() and not Cargo:IsDeployed() then
local CargoCoordinate = Cargo:GetCoordinate()
local CoordinateFree = true
for APC, Coordinate in pairs( self.PickupCargo ) do
if CargoCoordinate:Get2DDistance( Coordinate ) <= 25 then
CoordinateFree = false
break
end
end
if CoordinateFree == true then
self.PickupCargo[Carrier] = CargoCoordinate
PickupCargo = Cargo
break
end
end
end
if PickupCargo then
self.CarrierHome[Carrier] = nil
local PickupCoordinate = PickupCargo:GetCoordinate():GetRandomCoordinateInRadius( self.PickupOuterRadius, self.PickupInnerRadius )
AI_Cargo:Pickup( PickupCoordinate, math.random( self.PickupMinSpeed, self.PickupMaxSpeed ) )
break
else
if self.HomeZone then
if not self.CarrierHome[Carrier] then
self.CarrierHome[Carrier] = true
AI_Cargo:__Home( 10, self.HomeZone:GetRandomPointVec2() )
end
end
end
end
end
self:__Monitor( self.MonitorTimeInterval )
return self
end
--- Make a APC run for a cargo deploy action after the cargo Pickup trigger has been initiated, by default.
-- @param #AI_CARGO_DISPATCHER self
-- @param Wrapper.Group#GROUP APC
-- @return #AI_CARGO_DISPATCHER
function AI_CARGO_DISPATCHER:onafterPickup( From, Event, To, APC, Cargo )
return self
end
--- Make a APC run for a cargo deploy action after the cargo has been loaded, by default.
-- @param #AI_CARGO_DISPATCHER self
-- @param Wrapper.Group#GROUP APC
-- @return #AI_CARGO_DISPATCHER
function AI_CARGO_DISPATCHER:OnAfterLoaded( From, Event, To, APC, Cargo )
self:I( { "Loaded Dispatcher", APC } )
local DeployZone = self.SetDeployZones:GetRandomZone()
self:I( { RandomZone = DeployZone } )
local DeployCoordinate = DeployZone:GetCoordinate():GetRandomCoordinateInRadius( self.DeployOuterRadius, self.DeployInnerRadius )
self.AI_Cargo[APC]:Deploy( DeployCoordinate, math.random( self.DeployMinSpeed, self.DeployMaxSpeed ) )
self.PickupCargo[APC] = nil
return self
end

View File

@@ -0,0 +1,67 @@
--- **AI** -- (R2.4) - Models the intelligent transportation of infantry and other cargo using APCs.
--
-- ===
--
-- ### Author: **FlightControl**
--
-- ===
--
-- @module AI_Cargo_Dispatcher_APC
--- @type AI_CARGO_DISPATCHER_APC
-- @extends AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER
--- # AI\_CARGO\_DISPATCHER\_APC class, extends @{Core.Base#BASE}
--
-- ===
--
-- AI\_CARGO\_DISPATCHER\_APC brings a dynamic cargo handling 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 uses the @{Cargo} capabilities within the MOOSE framework.
-- CARGO derived objects must be declared within the mission to make the AI\_CARGO\_DISPATCHER\_APC object recognize the cargo.
-- Please consult the @{Cargo} module for more information.
--
--
--
-- @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 SetAPC
-- @param Core.Set#SET_CARGO SetCargo
-- @param Core.Set#SET_ZONE SetDeployZone
-- @return #AI_CARGO_DISPATCHER_APC
-- @usage
--
-- -- Create a new cargo dispatcher
-- SetAPC = SET_GROUP:New():FilterPrefixes( "APC" ):FilterStart()
-- SetCargo = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart()
-- SetDeployZone = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart()
-- AICargoDispatcher = AI_CARGO_DISPATCHER_APC:New( SetAPC, SetCargo )
--
function AI_CARGO_DISPATCHER_APC:New( SetAPC, SetCargo, SetDeployZones )
local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:New( SetAPC, SetCargo, SetDeployZones ) ) -- #AI_CARGO_DISPATCHER_APC
self.CombatRadius = 500
self:SetDeploySpeed( 70, 120 )
self:SetPickupSpeed( 70, 120 )
self:SetPickupRadius( 0, 0 )
self:SetDeployRadius( 0, 0 )
self:Monitor( 1 )
return self
end
function AI_CARGO_DISPATCHER_APC:AICargo( APC, SetCargo )
return AI_CARGO_APC:New( APC, SetCargo, self.CombatRadius )
end

View File

@@ -0,0 +1,54 @@
--- **AI** -- (R2.4) - Models the intelligent transportation of infantry and other cargo using Planes.
--
-- ===
--
-- ### Author: **FlightControl**
--
-- ===
--
-- @module AI_Cargo_Dispatcher_Airplane
--- @type AI_CARGO_DISPATCHER_AIRPLANE
-- @extends AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER
--- # AI\_CARGO\_DISPATCHER\_AIRPLANE class, extends @{Core.Base#BASE}
--
-- ===
--
-- AI\_CARGO\_DISPATCHER\_AIRPLANE 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 uses the @{Cargo} capabilities within the MOOSE framework.
-- CARGO derived objects must be declared within the mission to make the AI\_CARGO\_DISPATCHER\_AIRPLANE object recognize the cargo.
-- Please consult the @{Cargo} module for more information.
--
--
--
-- @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 SetAirplane
-- @param Core.Set#SET_CARGO SetCargo
-- @param Core.Set#SET_ZONE SetDeployZone
-- @return #AI_CARGO_DISPATCHER_AIRPLANE
-- @usage
--
-- -- Create a new cargo dispatcher
-- SetAirplane = SET_GROUP:New():FilterPrefixes( "Airplane" ):FilterStart()
-- SetCargo = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart()
-- SetDeployZone = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart()
-- AICargoDispatcher = AI_CARGO_DISPATCHER_AIRPLANE:New( SetAirplane, SetCargo )
--
function AI_CARGO_DISPATCHER_AIRPLANE:New( SetAirplane, SetCargo, SetDeployZones )
local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:New( SetAirplane, SetCargo, SetDeployZones ) ) -- #AI_CARGO_DISPATCHER_AIRPLANE
return self
end

View File

@@ -0,0 +1,65 @@
--- **AI** -- (R2.4) - Models the intelligent transportation of infantry and other cargo using Helicopters.
--
-- ===
--
-- ### Author: **FlightControl**
--
-- ===
--
-- @module AI_Cargo_Dispatcher_Helicopter
--- @type AI_CARGO_DISPATCHER_HELICOPTER
-- @extends AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER
--- # AI\_CARGO\_DISPATCHER\_HELICOPTER class, extends @{Core.Base#BASE}
--
-- ===
--
-- AI\_CARGO\_DISPATCHER\_HELICOPTER brings a dynamic cargo handling capability for AI groups.
--
-- Helicopters can be mobilized to intelligently transport infantry and other cargo within the simulation.
-- The AI\_CARGO\_DISPATCHER\_HELICOPTER module uses the @{Cargo} capabilities within the MOOSE framework.
-- CARGO derived objects must be declared within the mission to make the AI\_CARGO\_DISPATCHER\_HELICOPTER object recognize the cargo.
-- Please consult the @{Cargo} module for more information.
--
--
--
-- @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 SetHelicopter
-- @param Core.Set#SET_CARGO SetCargo
-- @param Core.Set#SET_ZONE SetDeployZone
-- @return #AI_CARGO_DISPATCHER_HELICOPTER
-- @usage
--
-- -- Create a new cargo dispatcher
-- SetHelicopter = SET_GROUP:New():FilterPrefixes( "Helicopter" ):FilterStart()
-- SetCargo = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart()
-- SetDeployZone = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart()
-- AICargoDispatcher = AI_CARGO_DISPATCHER_HELICOPTER:New( SetHelicopter, SetCargo )
--
function AI_CARGO_DISPATCHER_HELICOPTER:New( SetHelicopter, SetCargo, SetDeployZones )
local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:New( SetHelicopter, SetCargo, SetDeployZones ) ) -- #AI_CARGO_DISPATCHER_HELICOPTER
self:SetDeploySpeed( 200, 150 )
self:SetPickupSpeed( 200, 150 )
self:SetPickupRadius( 0, 0 )
self:SetDeployRadius( 0, 0 )
self:Monitor( 1 )
return self
end
function AI_CARGO_DISPATCHER_HELICOPTER:AICargo( Helicopter, SetCargo )
return AI_CARGO_HELICOPTER:New( Helicopter, SetCargo )
end

View File

@@ -0,0 +1,685 @@
--- **AI** -- (R2.3) - Models the intelligent transportation of infantry (cargo).
--
-- ===
--
-- ### Author: **FlightControl**
--
-- ===
--
-- @module AI_Cargo_Helicopter
--- @type AI_CARGO_HELICOPTER
-- @extends Core.Fsm#FSM_CONTROLLABLE
--- # AI\_CARGO\_TROOPS class, extends @{Core.Base@BASE}
--
-- ===
--
-- @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
-- @param #number CombatRadius
-- @return #AI_CARGO_HELICOPTER
function AI_CARGO_HELICOPTER:New( Helicopter, CargoSet )
local self = BASE:Inherit( self, FSM_CONTROLLABLE:New() ) -- #AI_CARGO_HELICOPTER
self.CargoSet = CargoSet -- Cargo.CargoGroup#CARGO_GROUP
self.Zone = ZONE_GROUP:New( Helicopter:GetName(), Helicopter, 300 )
self:SetStartState( "Unloaded" )
self:AddTransition( "Unloaded", "Pickup", "*" )
self:AddTransition( "Loaded", "Deploy", "*" )
self:AddTransition( "Unloaded", "Load", "Boarding" )
self:AddTransition( "Boarding", "Board", "Boarding" )
self:AddTransition( "Boarding", "Loaded", "Loaded" )
self:AddTransition( "Loaded", "Unload", "Unboarding" )
self:AddTransition( "Unboarding", "Unboard", "Unboarding" )
self:AddTransition( "Unboarding", "Unloaded", "Unloaded" )
self:AddTransition( "*", "Landed", "*" )
self:AddTransition( "*", "Queue", "*" )
self:AddTransition( "*", "Orbit" , "*" )
self:AddTransition( "*", "Home" , "*" )
self:AddTransition( "*", "Destroyed", "Destroyed" )
--- 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
--- Pickup Trigger for AI_CARGO_HELICOPTER
-- @function [parent=#AI_CARGO_HELICOPTER] Pickup
-- @param #AI_CARGO_HELICOPTER self
-- @param Core.Point#COORDINATE Coordinate
--- Pickup Asynchronous Trigger for AI_CARGO_HELICOPTER
-- @function [parent=#AI_CARGO_HELICOPTER] __Pickup
-- @param #AI_CARGO_HELICOPTER self
-- @param #number Delay
-- @param Core.Point#COORDINATE Coordinate
--- 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
-- @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
--- Deploy Trigger for AI_CARGO_HELICOPTER
-- @function [parent=#AI_CARGO_HELICOPTER] Deploy
-- @param #AI_CARGO_HELICOPTER self
-- @param Core.Point#COORDINATE Coordinate
--- Deploy Asynchronous Trigger for AI_CARGO_HELICOPTER
-- @function [parent=#AI_CARGO_HELICOPTER] __Deploy
-- @param #AI_CARGO_HELICOPTER self
-- @param Core.Point#COORDINATE Coordinate
-- @param #number Delay
self:SetCarrier( Helicopter )
return self
end
function AI_CARGO_HELICOPTER:IsTransporting()
return self.Transporting == true
end
function AI_CARGO_HELICOPTER:IsRelocating()
return self.Relocating == true
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:OnEventHit( EventData )
local AICargoTroops = self:GetState( self, "AI_CARGO_HELICOPTER" )
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 Helicopter.
AICargoTroops:Unload()
end
end
end
function Helicopter:OnEventLand( EventData )
AICargo:Landed()
end
self.Coalition = self.Helicopter:GetCoalition()
self:SetControllable( Helicopter )
return self
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:onafterLanded( Helicopter, From, Event, To )
if Helicopter and Helicopter:IsAlive() then
if self.RoutePickup == true then
self:Load( Helicopter:GetPointVec2() )
self.RoutePickup = false
self.Relocating = true
end
if self.RouteDeploy == true then
self:Unload( true )
self.RouteDeploy = false
self.Transporting = false
self.Relocating = false
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 )
local HelicopterInZone = false
if Helicopter and Helicopter:IsAlive() then
local Distance = Coordinate:DistanceFromPointVec2( Helicopter:GetCoordinate() )
if Distance > 500 then
self:__Queue( -10, Coordinate )
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 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 )
else
self:__Queue( -10, Coordinate )
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:onafterOrbit( Helicopter, From, Event, To, Coordinate )
if Helicopter and Helicopter:IsAlive() then
if not self:IsTransporting() then
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 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 ), 0, CoordinateTo:GetRandomCoordinateInRadius( 800, 500 ) )
Route[#Route].task = Helicopter:TaskCombo( Tasks )
Route[#Route+1] = WaypointTo
-- Now route the helicopter
Helicopter:Route( Route, 0 )
end
end
end
--- @param #AI_CARGO_HELICOPTER self
-- @param Wrapper.Group#GROUP Helicopter
function AI_CARGO_HELICOPTER:onbeforeLoad( Helicopter, From, Event, To, Coordinate )
local Boarding = false
if Helicopter and Helicopter:IsAlive() then
self.BoardingCount = 0
if Helicopter and Helicopter:IsAlive() then
self.Helicopter_Cargo = {}
for _, HelicopterUnit in pairs( Helicopter:GetUnits() ) do
local HelicopterUnit = HelicopterUnit -- Wrapper.Unit#UNIT
for _, Cargo in pairs( self.CargoSet:GetSet() ) do
local Cargo = Cargo -- Cargo.Cargo#CARGO
self:F( { IsUnLoaded = Cargo:IsUnLoaded() } )
if Cargo:IsUnLoaded() then
if Cargo:IsInLoadRadius( HelicopterUnit:GetCoordinate() ) then
self:F( { "In radius", HelicopterUnit:GetName() } )
--Cargo:Ungroup()
Cargo:Board( HelicopterUnit, 25 )
self:__Board( 1, Cargo )
Boarding = true
-- So now this APCUnit has Cargo that is being loaded.
-- This will be used further in the logic to follow and to check cargo status.
self.Helicopter_Cargo[HelicopterUnit] = Cargo
break
end
end
end
end
end
end
return Boarding
end
--- @param #AI_CARGO_HELICOPTER self
-- @param Wrapper.Group#GROUP Helicopter
function AI_CARGO_HELICOPTER:onafterBoard( Helicopter, From, Event, To, Cargo )
self:F( { APC, From, Event, To, Cargo } )
if Helicopter and Helicopter:IsAlive() then
self:F({ IsLoaded = Cargo:IsLoaded() } )
if not Cargo:IsLoaded() then
self:__Board( 10, Cargo )
else
self:__Loaded( 1, Cargo )
end
end
end
--- @param #AI_CARGO_HELICOPTER self
-- @param Wrapper.Group#GROUP Helicopter
function AI_CARGO_HELICOPTER:onbeforeLoaded( Helicopter, From, Event, To, Cargo )
self:F( { APC, From, Event, To } )
local Loaded = true
if Helicopter and Helicopter:IsAlive() then
for HelicopterUnit, Cargo in pairs( self.Helicopter_Cargo ) do
local Cargo = Cargo -- Cargo.Cargo#CARGO
self:F( { IsLoaded = Cargo:IsLoaded(), IsDestroyed = Cargo:IsDestroyed() } )
if not Cargo:IsLoaded() and not Cargo:IsDestroyed() then
Loaded = false
end
end
end
return Loaded
end
--- @param #AI_CARGO_HELICOPTER self
-- @param Wrapper.Group#GROUP Helicopter
function AI_CARGO_HELICOPTER:onafterUnload( Helicopter, From, Event, To, Deployed )
if Helicopter and Helicopter:IsAlive() then
for _, HelicopterUnit in pairs( Helicopter:GetUnits() ) do
local HelicopterUnit = HelicopterUnit -- Wrapper.Unit#UNIT
for _, Cargo in pairs( HelicopterUnit:GetCargo() ) do
Cargo:UnBoard()
Cargo:SetDeployed( true )
self:__Unboard( 10, Cargo, Deployed )
end
end
end
end
--- @param #AI_CARGO_HELICOPTER self
-- @param Wrapper.Group#GROUP Helicopter
function AI_CARGO_HELICOPTER:onafterUnboard( Helicopter, From, Event, To, Cargo, Deployed )
if Helicopter and Helicopter:IsAlive() then
if not Cargo:IsUnLoaded() then
self:__Unboard( 10, Cargo, Deployed )
else
self:__Unloaded( 1, Cargo, Deployed )
end
end
end
--- @param #AI_CARGO_HELICOPTER self
-- @param Wrapper.Group#GROUP Helicopter
function AI_CARGO_HELICOPTER:onbeforeUnloaded( Helicopter, From, Event, To, Cargo, Deployed )
self:F( { APC, From, Event, To, Cargo:GetName(), Deployed = Deployed } )
local AllUnloaded = true
--Cargo:Regroup()
if Helicopter and Helicopter:IsAlive() then
for _, HelicopterUnit in pairs( Helicopter:GetUnits() ) do
local CargoCheck = self.Helicopter_Cargo[HelicopterUnit] -- Cargo.Cargo#CARGO
if CargoCheck then
self:F( { CargoCheck:GetName(), IsUnLoaded = CargoCheck:IsUnLoaded() } )
if CargoCheck:IsUnLoaded() == false then
AllUnloaded = false
break
end
end
end
if AllUnloaded == true then
if Deployed == true then
for HelicopterUnit, Cargo in pairs( self.Helicopter_Cargo ) do
local Cargo = Cargo -- Cargo.Cargo#CARGO
end
self.Helicopter_Cargo = {}
end
self.Helicopter = Helicopter
end
end
self:F( { AllUnloaded = AllUnloaded } )
return AllUnloaded
end
--- @param #AI_CARGO_HELICOPTER self
-- @param Wrapper.Group#GROUP Helicopter
function AI_CARGO_HELICOPTER:onafterUnloaded( Helicopter, From, Event, To, Cargo, Deployed )
self:Orbit( Helicopter:GetCoordinate(), 50 )
AI_CARGO_QUEUE[Helicopter] = nil
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:onafterPickup( Helicopter, From, Event, To, Coordinate )
if Helicopter and Helicopter:IsAlive() ~= nil then
Helicopter:Activate()
self.RoutePickup = true
Coordinate.y = math.random( 50, 200 )
local Route = {}
--- Calculate the target route point.
local CoordinateFrom = Helicopter:GetCoordinate()
local CoordinateTo = Coordinate
--- Create a route point of type air.
local WaypointFrom = CoordinateFrom:WaypointAir(
"RADIO",
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
150,
true
)
--- Create a route point of type air.
local WaypointTo = CoordinateTo:WaypointAir(
"RADIO",
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
150,
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.Transporting = true
end
end
function AI_CARGO_HELICOPTER:_Deploy( AICargoHelicopter, Coordinate )
AICargoHelicopter:__Queue( -10, Coordinate, 100 )
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:onafterDeploy( Helicopter, From, Event, To, Coordinate )
if Helicopter and Helicopter:IsAlive() ~= nil then
self.RouteDeploy = true
local Route = {}
--- Calculate the target route point.
Coordinate.y = math.random( 50, 200 )
--- Create a route point of type air.
local CoordinateFrom = Helicopter:GetCoordinate()
local WaypointFrom = CoordinateFrom:WaypointAir(
"RADIO",
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
150,
true
)
Route[#Route+1] = WaypointFrom
Route[#Route+1] = WaypointFrom
--- Create a route point of type air.
local CoordinateTo = Coordinate
local WaypointTo = CoordinateTo:WaypointAir(
"RADIO",
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
150,
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 = {}
Tasks[#Tasks+1] = Helicopter:TaskFunction( "AI_CARGO_HELICOPTER._Deploy", self, Coordinate )
Tasks[#Tasks+1] = Helicopter:TaskOrbitCircle( math.random( 30, 100 ), 0, 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, 1 )
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:onafterHome( Helicopter, From, Event, To, Coordinate )
if Helicopter and Helicopter:IsAlive() ~= nil then
self.RouteHome = true
local Route = {}
--- Calculate the target route point.
Coordinate.y = math.random( 50, 200 )
--- Create a route point of type air.
local CoordinateFrom = Helicopter:GetCoordinate()
local WaypointFrom = CoordinateFrom:WaypointAir(
"RADIO",
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
150,
true
)
Route[#Route+1] = WaypointFrom
--- Create a route point of type air.
local CoordinateTo = Coordinate
local WaypointTo = CoordinateTo:WaypointAir(
"RADIO",
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
150,
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

@@ -155,8 +155,7 @@ do -- ACT_ASSIGN_ACCEPT
-- @param #string Event -- @param #string Event
-- @param #string From -- @param #string From
-- @param #string To -- @param #string To
function ACT_ASSIGN_ACCEPT:onafterStart( ProcessUnit, From, Event, To ) function ACT_ASSIGN_ACCEPT:onafterStart( ProcessUnit, Task, From, Event, To )
self:F( { ProcessUnit, From, Event, To } )
self:__Assign( 1 ) self:__Assign( 1 )
end end
@@ -167,11 +166,8 @@ do -- ACT_ASSIGN_ACCEPT
-- @param #string Event -- @param #string Event
-- @param #string From -- @param #string From
-- @param #string To -- @param #string To
function ACT_ASSIGN_ACCEPT:onenterAssigned( ProcessUnit, From, Event, To ) function ACT_ASSIGN_ACCEPT:onenterAssigned( ProcessUnit, Task, From, Event, To )
self:F( { ProcessUnit, From, Event, To } )
local ProcessGroup = ProcessUnit:GetGroup()
self.Task:Assign( ProcessUnit, ProcessUnit:GetPlayerName() ) self.Task:Assign( ProcessUnit, ProcessUnit:GetPlayerName() )
end end
@@ -192,36 +188,26 @@ do -- ACT_ASSIGN_MENU_ACCEPT
--- Init. --- Init.
-- @param #ACT_ASSIGN_MENU_ACCEPT self -- @param #ACT_ASSIGN_MENU_ACCEPT self
-- @param #string TaskName
-- @param #string TaskBriefing -- @param #string TaskBriefing
-- @return #ACT_ASSIGN_MENU_ACCEPT self -- @return #ACT_ASSIGN_MENU_ACCEPT self
function ACT_ASSIGN_MENU_ACCEPT:New( TaskName, TaskBriefing ) function ACT_ASSIGN_MENU_ACCEPT:New( TaskBriefing )
-- Inherits from BASE -- Inherits from BASE
local self = BASE:Inherit( self, ACT_ASSIGN:New() ) -- #ACT_ASSIGN_MENU_ACCEPT local self = BASE:Inherit( self, ACT_ASSIGN:New() ) -- #ACT_ASSIGN_MENU_ACCEPT
self.TaskName = TaskName
self.TaskBriefing = TaskBriefing self.TaskBriefing = TaskBriefing
return self return self
end end
function ACT_ASSIGN_MENU_ACCEPT:Init( FsmAssign )
self.TaskName = FsmAssign.TaskName
self.TaskBriefing = FsmAssign.TaskBriefing
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. --- 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 #ACT_ASSIGN_MENU_ACCEPT self
-- @param #string TaskName
-- @param #string TaskBriefing -- @param #string TaskBriefing
-- @return #ACT_ASSIGN_MENU_ACCEPT self -- @return #ACT_ASSIGN_MENU_ACCEPT self
function ACT_ASSIGN_MENU_ACCEPT:Init( TaskName, TaskBriefing ) function ACT_ASSIGN_MENU_ACCEPT:Init( TaskBriefing )
self.TaskBriefing = TaskBriefing self.TaskBriefing = TaskBriefing
self.TaskName = TaskName
return self return self
end end
@@ -232,30 +218,31 @@ do -- ACT_ASSIGN_MENU_ACCEPT
-- @param #string Event -- @param #string Event
-- @param #string From -- @param #string From
-- @param #string To -- @param #string To
function ACT_ASSIGN_MENU_ACCEPT:onafterStart( ProcessUnit, From, Event, To ) function ACT_ASSIGN_MENU_ACCEPT:onafterStart( ProcessUnit, Task, From, Event, To )
self:F( { ProcessUnit, From, Event, To } )
self:GetCommandCenter():MessageTypeToGroup( "Access the radio menu to accept the task. You have 30 seconds or the assignment will be cancelled.", ProcessUnit:GetGroup(), MESSAGE.Type.Information ) 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 ProcessGroup = ProcessUnit:GetGroup() 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.Menu = MENU_GROUP:New( ProcessGroup, "Task " .. self.TaskName .. " acceptance" ) self:__Reject( 120, TaskGroup )
self.MenuAcceptTask = MENU_GROUP_COMMAND:New( ProcessGroup, "Accept task " .. self.TaskName, self.Menu, self.MenuAssign, self )
self.MenuRejectTask = MENU_GROUP_COMMAND:New( ProcessGroup, "Reject task " .. self.TaskName, self.Menu, self.MenuReject, self )
end end
--- Menu function. --- Menu function.
-- @param #ACT_ASSIGN_MENU_ACCEPT self -- @param #ACT_ASSIGN_MENU_ACCEPT self
function ACT_ASSIGN_MENU_ACCEPT:MenuAssign() function ACT_ASSIGN_MENU_ACCEPT:MenuAssign( TaskGroup )
self:__Assign( 1 ) self:__Assign( -1, TaskGroup )
end end
--- Menu function. --- Menu function.
-- @param #ACT_ASSIGN_MENU_ACCEPT self -- @param #ACT_ASSIGN_MENU_ACCEPT self
function ACT_ASSIGN_MENU_ACCEPT:MenuReject() function ACT_ASSIGN_MENU_ACCEPT:MenuReject( TaskGroup )
self:__Reject( 1 ) self:__Reject( -1, TaskGroup )
end end
--- StateMachine callback function --- StateMachine callback function
@@ -264,8 +251,7 @@ do -- ACT_ASSIGN_MENU_ACCEPT
-- @param #string Event -- @param #string Event
-- @param #string From -- @param #string From
-- @param #string To -- @param #string To
function ACT_ASSIGN_MENU_ACCEPT:onafterAssign( ProcessUnit, From, Event, To ) function ACT_ASSIGN_MENU_ACCEPT:onafterAssign( ProcessUnit, Task, From, Event, To, TaskGroup )
self:F( { ProcessUnit.UnitNameFrom, Event, To } )
self.Menu:Remove() self.Menu:Remove()
end end
@@ -276,13 +262,25 @@ do -- ACT_ASSIGN_MENU_ACCEPT
-- @param #string Event -- @param #string Event
-- @param #string From -- @param #string From
-- @param #string To -- @param #string To
function ACT_ASSIGN_MENU_ACCEPT:onafterReject( ProcessUnit, From, Event, To ) function ACT_ASSIGN_MENU_ACCEPT:onafterReject( ProcessUnit, Task, From, Event, To, TaskGroup )
self:F( { ProcessUnit.UnitName, From, Event, To } ) self:F( { TaskGroup = TaskGroup } )
self.Menu:Remove() self.Menu:Remove()
--TODO: need to resolve this problem ... it has to do with the events ... --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:UnAssignFromUnit( ProcessUnit )needs to become a callback funtion call upon the event
ProcessUnit:Destroy() 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
end -- ACT_ASSIGN_MENU_ACCEPT end -- ACT_ASSIGN_MENU_ACCEPT

View File

@@ -123,16 +123,20 @@ do -- ACT_ROUTE
--- Set a Cancel Menu item. --- Set a Cancel Menu item.
-- @param #ACT_ROUTE self -- @param #ACT_ROUTE self
-- @return #ACT_ROUTE -- @return #ACT_ROUTE
function ACT_ROUTE:SetMenuCancel( MenuGroup, MenuText, ParentMenu, MenuTime ) function ACT_ROUTE:SetMenuCancel( MenuGroup, MenuText, ParentMenu, MenuTime, MenuTag )
MENU_GROUP_COMMAND:New( self.CancelMenuGroupCommand = MENU_GROUP_COMMAND:New(
MenuGroup, MenuGroup,
MenuText, MenuText,
ParentMenu, ParentMenu,
self.MenuCancel, self.MenuCancel,
self self
):SetTime(MenuTime) ):SetTime( MenuTime ):SetTag( MenuTag )
ParentMenu:SetTime( MenuTime )
ParentMenu:Remove( MenuTime, MenuTag )
return self return self
end end
@@ -206,7 +210,9 @@ do -- ACT_ROUTE
function ACT_ROUTE:MenuCancel() function ACT_ROUTE:MenuCancel()
self:Cancel() self:F("Cancelled")
self.CancelMenuGroupCommand:Remove()
self:__Cancel( 1 )
end end
--- Task Events --- Task Events
@@ -238,10 +244,8 @@ do -- ACT_ROUTE
-- @param #string From -- @param #string From
-- @param #string To -- @param #string To
function ACT_ROUTE:onbeforeRoute( ProcessUnit, From, Event, To ) function ACT_ROUTE:onbeforeRoute( ProcessUnit, From, Event, To )
self:F( { "BeforeRoute 1", self.DisplayCount, self.DisplayInterval } )
if ProcessUnit:IsAlive() then if ProcessUnit:IsAlive() then
self:F( "BeforeRoute 2" )
local HasArrived = self:onfuncHasArrived( ProcessUnit ) -- Polymorphic local HasArrived = self:onfuncHasArrived( ProcessUnit ) -- Polymorphic
if self.DisplayCount >= self.DisplayInterval then if self.DisplayCount >= self.DisplayInterval then
self:T( { HasArrived = HasArrived } ) self:T( { HasArrived = HasArrived } )
@@ -253,8 +257,6 @@ do -- ACT_ROUTE
self.DisplayCount = self.DisplayCount + 1 self.DisplayCount = self.DisplayCount + 1
end end
self:T( { DisplayCount = self.DisplayCount } )
if HasArrived then if HasArrived then
self:__Arrive( 1 ) self:__Arrive( 1 )
else else
@@ -337,7 +339,7 @@ do -- ACT_ROUTE_POINT
-- @param #ACT_ROUTE_POINT self -- @param #ACT_ROUTE_POINT self
-- @param #number Range The Range to consider the arrival. Default is 10000 meters. -- @param #number Range The Range to consider the arrival. Default is 10000 meters.
function ACT_ROUTE_POINT:SetRange( Range ) function ACT_ROUTE_POINT:SetRange( Range )
self:F2( { self.Range } ) self:F2( { Range } )
self.Range = Range or 10000 self.Range = Range or 10000
end end
@@ -345,6 +347,7 @@ do -- ACT_ROUTE_POINT
-- @param #ACT_ROUTE_POINT self -- @param #ACT_ROUTE_POINT self
-- @return #number The Range to consider the arrival. Default is 10000 meters. -- @return #number The Range to consider the arrival. Default is 10000 meters.
function ACT_ROUTE_POINT:GetRange() function ACT_ROUTE_POINT:GetRange()
self:F2( { self.Range } )
return self.Range return self.Range
end end

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,316 @@
--- **Cargo** -- Management of single cargo crates, which are based on a @{Static} object.
--
-- ===
--
-- ![Banner Image](..\Presentations\CARGO\Dia1.JPG)
--
-- ===
--
-- ### [Demo Missions]()
--
-- ### [YouTube Playlist]()
--
-- ===
--
-- ### Author: **FlightControl**
-- ### Contributions:
--
-- ===
--
-- @module CargoCrate
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
--- # CARGO\_CRATE class, extends @{#CARGO_REPRESENTABLE}
--
-- The CARGO\_CRATE class 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.
--
-- ===
--
-- @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:F( { 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.PlayerLeaveUnit, self.OnEventCargoDead )
self:SetEventPriority( 4 )
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:F( { 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:F( { 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()
--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 Cargo Crate is in the radius for the Cargo to be reported.
-- @param #CARGO self
-- @param Core.Point#COORDINATE Coordinate
-- @return #boolean true if the Cargo Crate is within the report radius.
function CARGO_CRATE:IsInReportRadius( Coordinate )
--self:F( { 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 self
-- @param Core.Point#Coordinate Coordinate
-- @return #boolean true if the Cargo Crate is within the loading radius.
function CARGO_CRATE:IsInLoadRadius( Coordinate )
--self:F( { 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:F()
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:F( {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:F( {NearRadius = NearRadius } )
return self:IsNear( CargoCarrier:GetCoordinate(), NearRadius )
end
--- Respawn the CargoGroup.
-- @param #CARGO_CRATE self
function CARGO_CRATE:Respawn()
self:F( { "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:F( { "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

@@ -0,0 +1,770 @@
--- **Cargo** -- Management of grouped cargo logistics, which are based on a @{Group} object.
--
-- ===
--
-- ![Banner Image](..\Presentations\CARGO\Dia1.JPG)
--
-- ===
--
-- ### [Demo Missions]()
--
-- ### [YouTube Playlist]()
--
-- ===
--
-- ### Author: **FlightControl**
-- ### Contributions:
--
-- ===
--
-- @module CargoGroup
do -- CARGO_GROUP
--- @type CARGO_GROUP
-- @extends Cargo.Cargo#CARGO_REPORTABLE
-- @field Core.Set#SET_CARGO CargoSet The collection of derived CARGO objects.
-- @field #string GroupName The name of the CargoGroup.
--- # CARGO\_GROUP class
--
-- The CARGO\_GROUP class defines a cargo that is represented by a @{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 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#AI_CARGO_APC} class.
-- * AI Helicopters to transport cargo, using the @{AI.AI_Cargo_Helicopter#AI_CARGO_HELICOPTER} class.
-- * AI Planes to transport cargo, using the @{AI.AI_Cargo_Plane#AI_CARGO_PLANE} class.
-- * 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.
--
-- The
--
-- @field #CARGO_GROUP CARGO_GROUP
--
CARGO_GROUP = {
ClassName = "CARGO_GROUP",
}
--- CARGO_GROUP constructor.
-- This make a new CARGO_GROUP from a @{Group} object.
-- It will "ungroup" the group object within the sim, and will create a @{Set} of individual Unit objects.
-- @param #CARGO_GROUP self
-- @param Wrapper.Group#GROUP CargoGroup
-- @param #string Type
-- @param #string Name
-- @param #number LoadRadius (optional)
-- @param #number NearRadius (optional)
-- @return #CARGO_GROUP
function CARGO_GROUP:New( CargoGroup, Type, Name, LoadRadius )
local self = BASE:Inherit( self, CARGO_REPORTABLE:New( Type, Name, 0, LoadRadius ) ) -- #CARGO_GROUP
self:F( { Type, Name, LoadRadius } )
self.CargoSet = SET_CARGO:New()
self.CargoGroup = CargoGroup
self.Grouped = true
self.CargoUnitTemplate = {}
self:SetDeployed( false )
local WeightGroup = 0
self.CargoGroup:Destroy()
local GroupName = CargoGroup:GetName()
self.CargoName = GroupName:match("(.*)~CARGO") or GroupName
self.CargoTemplate = UTILS.DeepCopy( _DATABASE:GetGroupTemplate( GroupName ) )
local GroupTemplate = UTILS.DeepCopy( self.CargoTemplate )
GroupTemplate.name = self.CargoName .. "#CARGO"
GroupTemplate.groupId = nil
GroupTemplate.units = {}
for UnitID, UnitTemplate in pairs( self.CargoTemplate.units ) do
UnitTemplate.name = UnitTemplate.name .. "#CARGO"
local CargoUnitName = UnitTemplate.name
self.CargoUnitTemplate[CargoUnitName] = UnitTemplate
GroupTemplate.units[#GroupTemplate.units+1] = self.CargoUnitTemplate[CargoUnitName]
GroupTemplate.units[#GroupTemplate.units].unitId = nil
-- And we register the spawned unit as part of the CargoSet.
local Unit = UNIT:Register( CargoUnitName )
--local WeightUnit = Unit:GetDesc().massEmpty
--WeightGroup = WeightGroup + WeightUnit
local CargoUnit = CARGO_UNIT:New( Unit, Type, CargoUnitName, 10 )
self.CargoSet:Add( CargoUnitName, CargoUnit )
end
-- Then we register the new group in the database
self.CargoGroup = GROUP:NewTemplate( GroupTemplate, GroupTemplate.CoalitionID, GroupTemplate.CategoryID, GroupTemplate.CountryID)
-- Now we spawn the new group based on the template created.
_DATABASE:Spawn( GroupTemplate )
self:SetWeight( WeightGroup )
self.CargoLimit = 10
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.PlayerLeaveUnit, self.OnEventCargoDead )
self:SetEventPriority( 4 )
return self
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
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:F("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:F( { 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:F( { "Regroup", GroupTemplate } )
-- Now we spawn the new group based on the template created.
_DATABASE:Spawn( GroupTemplate )
end
end
--- @param #CARGO_GROUP self
-- @param Core.Event#EVENTDATA EventData
function CARGO_GROUP:OnEventCargoDead( EventData )
self:I( 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
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:E( { "Cargo group destroyed" } )
end
end
--- Enter Boarding State.
-- @param #CARGO_GROUP self
-- @param Wrapper.Unit#UNIT CargoCarrier
-- @param #string Event
-- @param #string From
-- @param #string To
function CARGO_GROUP:onenterBoarding( From, Event, To, CargoCarrier, NearRadius, ... )
--self:F( { CargoCarrier.UnitName, From, Event, To } )
local NearRadius = NearRadius or 25
if From == "UnLoaded" then
-- For each Cargo object within the CARGO_GROUPED, route each object to the CargoLoadPointVec2
self.CargoSet:ForEach(
function( Cargo, ... )
Cargo:__Board( 1, CargoCarrier, NearRadius, ... )
end, ...
)
self:__Boarding( 1, CargoCarrier, NearRadius, ... )
end
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:onenterLoaded( From, Event, To, CargoCarrier, ... )
--self:F( { 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
Cargo:Load( CargoCarrier )
end
end
--self.CargoObject:Destroy()
self.CargoCarrier = CargoCarrier
self.CargoCarrier:AddCargo( self )
end
--- Leave Boarding State.
-- @param #CARGO_GROUP self
-- @param Wrapper.Unit#UNIT CargoCarrier
-- @param #string Event
-- @param #string From
-- @param #string To
function CARGO_GROUP:onafterBoarding( From, Event, To, CargoCarrier, NearRadius, ... )
--self:F( { CargoCarrier.UnitName, From, Event, To } )
local NearRadius = NearRadius or 100
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( 1, CargoCarrier, NearRadius, ... )
else
self:F("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 Core.Point#POINT_VEC2 ToPointVec2
-- @param #string Event
-- @param #string From
-- @param #string To
function CARGO_GROUP:onenterUnBoarding( From, Event, To, ToPointVec2, NearRadius, ... )
--self:F( {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
Cargo:__UnBoard( Timer, ToPointVec2, NearRadius )
Timer = Timer + 3
end
end, { NearRadius }
)
self:__UnBoarding( 1, ToPointVec2, NearRadius, ... )
end
end
--- Leave UnBoarding State.
-- @param #CARGO_GROUP self
-- @param Core.Point#POINT_VEC2 ToPointVec2
-- @param #string Event
-- @param #string From
-- @param #string To
function CARGO_GROUP:onleaveUnBoarding( From, Event, To, ToPointVec2, NearRadius, ... )
--self:F( { 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
return true
else
self:__UnBoarding( 1, ToPointVec2, NearRadius, ... )
end
return false
end
end
--- UnBoard Event.
-- @param #CARGO_GROUP self
-- @param Core.Point#POINT_VEC2 ToPointVec2
-- @param #string Event
-- @param #string From
-- @param #string To
function CARGO_GROUP:onafterUnBoarding( From, Event, To, ToPointVec2, NearRadius, ... )
--self:F( { From, Event, To, ToPointVec2, NearRadius } )
--local NearRadius = NearRadius or 25
self:__UnLoad( 1, ToPointVec2, ... )
end
--- Enter UnLoaded State.
-- @param #CARGO_GROUP self
-- @param Core.Point#POINT_VEC2
-- @param #string Event
-- @param #string From
-- @param #string To
function CARGO_GROUP:onenterUnLoaded( From, Event, To, ToPointVec2, ... )
--self:F( { 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=ToPointVec2:GetRandomPointVec2InRadius(10)
Cargo:UnLoad( 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()
self:F()
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 amount of cargo units in the 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:F( {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.
-- @return #nil The Cargo is not near to the Carrier.
function CARGO_GROUP:IsNear( CargoCarrier, NearRadius )
self:F( {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:F( "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:F( { Coordinate } )
local Cargo = self:GetFirstAlive() -- Cargo.Cargo#CARGO
if Cargo then
local Distance = 0
if Cargo:IsLoaded() then
Distance = Coordinate:Get2DDistance( Cargo.CargoCarrier:GetCoordinate() )
else
Distance = Coordinate:Get2DDistance( Cargo.CargoObject:GetCoordinate() )
end
self:F( { 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:F( { Coordinate } )
local Cargo = self:GetFirstAlive() -- Cargo.Cargo#CARGO
if Cargo then
self:F( { 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
--- Respawn the CargoGroup.
-- @param #CARGO_GROUP self
function CARGO_GROUP:Respawn()
self:F( { "Respawning" } )
for CargoID, CargoData in pairs( self.CargoSet:GetSet() ) do
local Cargo = CargoData -- #CARGO
Cargo:Destroy()
Cargo:SetStartState( "UnLoaded" )
end
-- We iterate through the group template and for each unit in the template, we create a new group with one unit.
for UnitID, UnitTemplate in pairs( self.CargoTemplate.units ) do
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 = GroupName .. "#CARGO#" .. UnitID
GroupTemplate.groupId = nil
GroupTemplate.units = {}
GroupTemplate.units[1] = UnitTemplate
local UnitName = UnitTemplate.name .. "#CARGO"
GroupTemplate.units[1].name = UnitTemplate.name .. "#CARGO"
-- 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 )
-- And we register the spawned unit as part of the CargoSet.
local Unit = UNIT:FindByName( UnitName )
--local WeightUnit = Unit:GetDesc().massEmpty
--WeightGroup = WeightGroup + WeightUnit
local CargoUnit = CARGO_UNIT:New( Unit, Type, UnitName, 10 )
self.CargoSet:Add( UnitName, CargoUnit )
end
self:SetDeployed( false )
self:SetStartState( "UnLoaded" )
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
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
if Cargo then
Cargo:Smoke( SmokeColor, Radius )
end
end
--- Check if the first element of the CargoGroup is the given @{Zone}.
-- @param #CARGO 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:F( { Zone } )
local Cargo = self.CargoSet:GetFirst() -- #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

@@ -0,0 +1,267 @@
--- **Cargo** -- Management of single cargo crates, which are based on a @{Static} object. The cargo can only be slingloaded.
--
-- ===
--
-- ![Banner Image](..\Presentations\CARGO\Dia1.JPG)
--
-- ===
--
-- ### [Demo Missions]()
--
-- ### [YouTube Playlist]()
--
-- ===
--
-- ### Author: **FlightControl**
-- ### Contributions:
--
-- ===
--
-- @module CargoCrate
do -- CARGO_SLINGLOAD
--- Models the behaviour of cargo crates, which can only be slingloaded.
-- @type CARGO_SLINGLOAD
-- @extends Cargo.Cargo#CARGO_REPRESENTABLE
--- # CARGO\_CRATE class, extends @{#CARGO_REPRESENTABLE}
--
-- The CARGO\_CRATE class defines a cargo that is represented by a UNIT object within the simulator, and can be transported by a carrier.
--
-- ===
--
-- @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:F( { 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.PlayerLeaveUnit, self.OnEventCargoDead )
self:SetEventPriority( 4 )
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 self
function CARGO: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:F( { 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:F( { 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:F()
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:F( {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:F( {NearRadius = NearRadius } )
return self:IsNear( CargoCarrier:GetCoordinate(), NearRadius )
end
--- Respawn the CargoGroup.
-- @param #CARGO_SLINGLOAD self
function CARGO_SLINGLOAD:Respawn()
--self:F( { "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:F( { "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

@@ -0,0 +1,398 @@
--- **Cargo** -- Management of single cargo logistics, which are based on a @{Unit} object.
--
-- ===
--
-- ![Banner Image](..\Presentations\CARGO\Dia1.JPG)
--
-- ===
--
-- ### [Demo Missions]()
--
-- ### [YouTube Playlist]()
--
-- ===
--
-- ### Author: **FlightControl**
-- ### Contributions:
--
-- ===
--
-- @module CargoUnit
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
--- # CARGO\_UNIT class, extends @{#CARGO_REPRESENTABLE}
--
-- The CARGO\_UNIT class 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.
--
-- ===
--
-- @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, Weight, NearRadius )
local self = BASE:Inherit( self, CARGO_REPRESENTABLE:New( CargoUnit, Type, Name, Weight, NearRadius ) ) -- #CARGO_UNIT
self:I( { Type, Name, Weight, NearRadius } )
self:T( CargoUnit )
self.CargoObject = CargoUnit
self:T( self.ClassName )
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
function CARGO_UNIT:onenterUnBoarding( From, Event, To, ToPointVec2, NearRadius )
self:F( { From, Event, To, ToPointVec2, NearRadius } )
NearRadius = NearRadius or 25
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
self.CargoObject:ReSpawnAt( FromPointVec2, CargoDeployHeading )
self:F( { "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
function CARGO_UNIT:onleaveUnBoarding( From, Event, To, ToPointVec2, NearRadius )
self:F( { From, Event, To, ToPointVec2, NearRadius } )
NearRadius = NearRadius or 100
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
function CARGO_UNIT:onafterUnBoarding( From, Event, To, ToPointVec2, NearRadius )
self:F( { From, Event, To, ToPointVec2, NearRadius } )
NearRadius = NearRadius or 100
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:F( { 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
function CARGO_UNIT:onafterBoard( From, Event, To, CargoCarrier, NearRadius, ... )
self:F( { From, Event, To, CargoCarrier, NearRadius } )
local NearRadius = NearRadius or 25
self.CargoInAir = self.CargoObject:InAir()
local Desc = self.CargoObject:GetDesc()
local MaxSpeed = Desc.speedMaxOffRoad
local TypeName = Desc.typeName
self:T( self.CargoInAir )
-- 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 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 = 5
NearRadius = NearRadius or 25
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( -1, 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
function CARGO_UNIT:onafterBoarding( From, Event, To, CargoCarrier, NearRadius, ... )
--self:F( { From, Event, To, CargoCarrier.UnitName, NearRadius } )
if CargoCarrier and CargoCarrier:IsAlive() and self.CargoObject and self.CargoObject:IsAlive() then
if CargoCarrier:InAir() == false then
if self:IsNear( CargoCarrier:GetPointVec2(), NearRadius ) then
self:__Load( 1, CargoCarrier, ... )
else
self:__Boarding( -1, CargoCarrier, NearRadius, ... )
self.RunCount = self.RunCount + 1
if self.RunCount >= 40 then
self.RunCount = 0
local Speed = 90
local Angle = 180
local Distance = 5
NearRadius = NearRadius or 25
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:E("Something is wrong")
end
end
--- Enter Boarding State.
-- @param #CARGO_UNIT self
-- @param #string Event
-- @param #string From
-- @param #string To
-- @param Wrapper.Unit#UNIT CargoCarrier
function CARGO_UNIT:onenterBoarding( From, Event, To, CargoCarrier, NearRadius, ... )
--self:F( { From, Event, To, CargoCarrier.UnitName, NearRadius } )
local Speed = 90
local Angle = 180
local Distance = 5
local NearRadius = NearRadius or 25
if From == "UnLoaded" or From == "Boarding" then
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:F( { 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.CargoObject:Destroy()
--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

@@ -645,6 +645,22 @@ function BASE:CreateEventCrash( EventTime, Initiator )
world.onEvent( Event ) world.onEvent( Event )
end end
--- Creation of a Dead Event.
-- @param #BASE self
-- @param Dcs.DCSTypes#Time EventTime The time stamp of the event.
-- @param Dcs.DCSWrapper.Object#Object Initiator The initiating object of the event.
function BASE:CreateEventDead( EventTime, Initiator )
self:F( { EventTime, Initiator } )
local Event = {
id = world.event.S_EVENT_DEAD,
time = EventTime,
initiator = Initiator,
}
world.onEvent( Event )
end
--- Creation of a Takeoff Event. --- Creation of a Takeoff Event.
-- @param #BASE self -- @param #BASE self
-- @param Dcs.DCSTypes#Time EventTime The time stamp of the event. -- @param Dcs.DCSTypes#Time EventTime The time stamp of the event.

File diff suppressed because it is too large Load Diff

View File

@@ -58,6 +58,7 @@ DATABASE = {
ZONENAMES = {}, ZONENAMES = {},
HITS = {}, HITS = {},
DESTROYS = {}, DESTROYS = {},
ZONES = {},
} }
local _DATABASECoalition = local _DATABASECoalition =
@@ -242,35 +243,146 @@ function DATABASE:FindAirbase( AirbaseName )
return AirbaseFound return AirbaseFound
end end
--- Adds a Cargo based on the Cargo Name in the DATABASE.
-- @param #DATABASE self
-- @param #string CargoName The name of the airbase
function DATABASE:AddCargo( Cargo )
if not self.CARGOS[Cargo.Name] then do -- Zones
self.CARGOS[Cargo.Name] = Cargo
--- Finds a @{Zone} based on the zone name.
-- @param #DATABASE self
-- @param #string ZoneName The name of the zone.
-- @return Core.Zone#ZONE_BASE The found ZONE.
function DATABASE:FindZone( ZoneName )
local ZoneFound = self.ZONES[ZoneName]
return ZoneFound
end end
--- Adds a @{Zone} based on the zone name in the DATABASE.
-- @param #DATABASE self
-- @param #string ZoneName The name of the zone.
-- @param Core.Zone#ZONE_BASE Zone The zone.
function DATABASE:AddZone( ZoneName, Zone )
if not self.ZONES[ZoneName] then
self.ZONES[ZoneName] = Zone
end
end
--- Deletes a @{Zone} from the DATABASE based on the zone name.
-- @param #DATABASE self
-- @param #string ZoneName The name of the zone.
function DATABASE:DeleteZone( ZoneName )
self.ZONES[ZoneName] = nil
end
--- Finds an @{Zone} based on the zone name in the DATABASE.
-- @param #DATABASE self
-- @param #string ZoneName
-- @return Core.Zone#ZONE_BASE The found @{Zone}.
function DATABASE:FindZone( ZoneName )
local ZoneFound = self.ZONES[ZoneName]
return ZoneFound
end
end end
--- Deletes a Cargo from the DATABASE based on the Cargo Name. do -- cargo
-- @param #DATABASE self
-- @param #string CargoName The name of the airbase
function DATABASE:DeleteCargo( CargoName )
self.CARGOS[CargoName] = nil --- Adds a Cargo based on the Cargo Name in the DATABASE.
end -- @param #DATABASE self
-- @param #string CargoName The name of the airbase
function DATABASE:AddCargo( Cargo )
if not self.CARGOS[Cargo.Name] then
self.CARGOS[Cargo.Name] = Cargo
end
end
--- Deletes a Cargo from the DATABASE based on the Cargo Name.
-- @param #DATABASE self
-- @param #string CargoName The name of the airbase
function DATABASE:DeleteCargo( CargoName )
self.CARGOS[CargoName] = nil
end
--- Finds an CARGO based on the CargoName.
-- @param #DATABASE self
-- @param #string CargoName
-- @return Wrapper.Cargo#CARGO The found CARGO.
function DATABASE:FindCargo( CargoName )
local CargoFound = self.CARGOS[CargoName]
return CargoFound
end
--- Checks if the Template name has a ~CARGO tag.
-- If yes, the group is a cargo.
-- @param #DATABASE self
-- @param #string TemplateName
-- @return #boolean
function DATABASE:IsCargo( TemplateName )
--- Finds an CARGO based on the CargoName. TemplateName = env.getValueDictByKey( TemplateName )
-- @param #DATABASE self
-- @param #string CargoName local Cargo = TemplateName:match( "~(CARGO)" )
-- @return Wrapper.Cargo#CARGO The found CARGO.
function DATABASE:FindCargo( CargoName )
local CargoFound = self.CARGOS[CargoName] return Cargo and Cargo == "CARGO"
return CargoFound end
end
--- Private method that registers new Static Templates within the DATABASE Object.
-- @param #DATABASE self
-- @return #DATABASE self
function DATABASE:RegisterCargos()
for CargoGroupName, CargoGroup in pairs( self.GROUPS ) do
if self:IsCargo( CargoGroupName ) then
local CargoInfo = CargoGroupName:match("~CARGO(.*)")
local CargoParam = CargoInfo and CargoInfo:match( "%((.*)%)")
local CargoName = CargoGroupName:match("(.*)~CARGO")
local Type = CargoParam and CargoParam:match( "T=([%a%d ]+),?")
local Name = CargoParam and CargoParam:match( "N=([%a%d]+),?") or CargoName
local LoadRadius = CargoParam and tonumber( CargoParam:match( "RR=([%a%d]+),?") )
local NearRadius = CargoParam and tonumber( CargoParam:match( "NR=([%a%d]+),?") )
self:F({"Register CargoGroup:",Type=Type,Name=Name,LoadRadius=LoadRadius,NearRadius=NearRadius})
CARGO_GROUP:New( CargoGroup, Type, Name, LoadRadius, NearRadius )
end
end
for CargoStaticName, CargoStatic in pairs( self.STATICS ) do
if self:IsCargo( CargoStaticName ) then
local CargoInfo = CargoStaticName:match("~CARGO(.*)")
local CargoParam = CargoInfo and CargoInfo:match( "%((.*)%)")
local CargoName = CargoStaticName:match("(.*)~CARGO")
local Type = CargoParam and CargoParam:match( "T=([%a%d ]+),?")
local Category = CargoParam and CargoParam:match( "C=([%a%d ]+),?")
local Name = CargoParam and CargoParam:match( "N=([%a%d]+),?") or CargoName
local LoadRadius = CargoParam and tonumber( CargoParam:match( "RR=([%a%d]+),?") )
local NearRadius = CargoParam and tonumber( CargoParam:match( "NR=([%a%d]+),?") )
if Category == "SLING" then
self:F({"Register CargoSlingload:",Type=Type,Name=Name,LoadRadius=LoadRadius,NearRadius=NearRadius})
CARGO_SLINGLOAD:New( CargoStatic, Type, Name, LoadRadius, NearRadius )
else
if Category == "CRATE" then
self:F({"Register CargoCrate:",Type=Type,Name=Name,LoadRadius=LoadRadius,NearRadius=NearRadius})
CARGO_CRATE:New( CargoStatic, Type, Name, LoadRadius, NearRadius )
end
end
end
end
end
end -- cargo
--- Finds a CLIENT based on the ClientName. --- Finds a CLIENT based on the ClientName.
-- @param #DATABASE self -- @param #DATABASE self
@@ -450,8 +562,6 @@ function DATABASE:_RegisterGroupTemplate( GroupTemplate, CoalitionSide, Category
local GroupTemplateName = GroupName or env.getValueDictByKey( GroupTemplate.name ) local GroupTemplateName = GroupName or env.getValueDictByKey( GroupTemplate.name )
local TraceTable = {}
if not self.Templates.Groups[GroupTemplateName] then if not self.Templates.Groups[GroupTemplateName] then
self.Templates.Groups[GroupTemplateName] = {} self.Templates.Groups[GroupTemplateName] = {}
self.Templates.Groups[GroupTemplateName].Status = nil self.Templates.Groups[GroupTemplateName].Status = nil
@@ -475,18 +585,7 @@ function DATABASE:_RegisterGroupTemplate( GroupTemplate, CoalitionSide, Category
self.Templates.Groups[GroupTemplateName].CoalitionID = CoalitionSide self.Templates.Groups[GroupTemplateName].CoalitionID = CoalitionSide
self.Templates.Groups[GroupTemplateName].CountryID = CountryID self.Templates.Groups[GroupTemplateName].CountryID = CountryID
local UnitNames = {}
TraceTable[#TraceTable+1] = "Group"
TraceTable[#TraceTable+1] = self.Templates.Groups[GroupTemplateName].GroupName
TraceTable[#TraceTable+1] = "Coalition"
TraceTable[#TraceTable+1] = self.Templates.Groups[GroupTemplateName].CoalitionID
TraceTable[#TraceTable+1] = "Category"
TraceTable[#TraceTable+1] = self.Templates.Groups[GroupTemplateName].CategoryID
TraceTable[#TraceTable+1] = "Country"
TraceTable[#TraceTable+1] = self.Templates.Groups[GroupTemplateName].CountryID
TraceTable[#TraceTable+1] = "Units"
for unit_num, UnitTemplate in pairs( GroupTemplate.units ) do for unit_num, UnitTemplate in pairs( GroupTemplate.units ) do
@@ -510,10 +609,16 @@ function DATABASE:_RegisterGroupTemplate( GroupTemplate, CoalitionSide, Category
self.Templates.ClientsByID[UnitTemplate.unitId] = UnitTemplate self.Templates.ClientsByID[UnitTemplate.unitId] = UnitTemplate
end end
TraceTable[#TraceTable+1] = self.Templates.Units[UnitTemplate.name].UnitName UnitNames[#UnitNames+1] = self.Templates.Units[UnitTemplate.name].UnitName
end end
self:E( TraceTable ) self:I( { Group = self.Templates.Groups[GroupTemplateName].GroupName,
Coalition = self.Templates.Groups[GroupTemplateName].CoalitionID,
Category = self.Templates.Groups[GroupTemplateName].CategoryID,
Country = self.Templates.Groups[GroupTemplateName].CountryID,
Units = UnitNames
}
)
end end
function DATABASE:GetGroupTemplate( GroupName ) function DATABASE:GetGroupTemplate( GroupName )
@@ -530,8 +635,6 @@ end
-- @return #DATABASE self -- @return #DATABASE self
function DATABASE:_RegisterStaticTemplate( StaticTemplate, CoalitionID, CategoryID, CountryID ) function DATABASE:_RegisterStaticTemplate( StaticTemplate, CoalitionID, CategoryID, CountryID )
local TraceTable = {}
local StaticTemplateName = env.getValueDictByKey(StaticTemplate.name) local StaticTemplateName = env.getValueDictByKey(StaticTemplate.name)
self.Templates.Statics[StaticTemplateName] = self.Templates.Statics[StaticTemplateName] or {} self.Templates.Statics[StaticTemplateName] = self.Templates.Statics[StaticTemplateName] or {}
@@ -547,28 +650,22 @@ function DATABASE:_RegisterStaticTemplate( StaticTemplate, CoalitionID, Category
self.Templates.Statics[StaticTemplateName].CoalitionID = CoalitionID self.Templates.Statics[StaticTemplateName].CoalitionID = CoalitionID
self.Templates.Statics[StaticTemplateName].CountryID = CountryID self.Templates.Statics[StaticTemplateName].CountryID = CountryID
self:I( { Static = self.Templates.Statics[StaticTemplateName].StaticName,
Coalition = self.Templates.Statics[StaticTemplateName].CoalitionID,
Category = self.Templates.Statics[StaticTemplateName].CategoryID,
Country = self.Templates.Statics[StaticTemplateName].CountryID
}
)
self:AddStatic( StaticTemplateName )
TraceTable[#TraceTable+1] = "Static"
TraceTable[#TraceTable+1] = self.Templates.Statics[StaticTemplateName].StaticName
TraceTable[#TraceTable+1] = "Coalition"
TraceTable[#TraceTable+1] = self.Templates.Statics[StaticTemplateName].CoalitionID
TraceTable[#TraceTable+1] = "Category"
TraceTable[#TraceTable+1] = self.Templates.Statics[StaticTemplateName].CategoryID
TraceTable[#TraceTable+1] = "Country"
TraceTable[#TraceTable+1] = self.Templates.Statics[StaticTemplateName].CountryID
self:E( TraceTable )
end end
--- @param #DATABASE self --- @param #DATABASE self
function DATABASE:GetStaticUnitTemplate( StaticName ) function DATABASE:GetStaticUnitTemplate( StaticName )
local StaticTemplate = self.Templates.Statics[StaticName].UnitTemplate local StaticTemplate = self.Templates.Statics[StaticName].UnitTemplate
StaticTemplate.SpawnCoalitionID = self.Templates.Statics[StaticName].CoalitionID return StaticTemplate, self.Templates.Statics[StaticName].CoalitionID, self.Templates.Statics[StaticName].CategoryID, self.Templates.Statics[StaticName].CountryID
StaticTemplate.SpawnCategoryID = self.Templates.Statics[StaticName].CategoryID
StaticTemplate.SpawnCountryID = self.Templates.Statics[StaticName].CountryID
return StaticTemplate
end end
@@ -703,7 +800,7 @@ function DATABASE:_RegisterAirbases()
local DCSAirbaseName = DCSAirbase:getName() local DCSAirbaseName = DCSAirbase:getName()
self:E( { "Register Airbase:", DCSAirbaseName } ) self:E( { "Register Airbase:", DCSAirbaseName, DCSAirbase:getID() } )
self:AddAirbase( DCSAirbaseName ) self:AddAirbase( DCSAirbaseName )
end end
end end
@@ -734,7 +831,7 @@ function DATABASE:_EventOnBirth( Event )
Event.IniGroup = self:FindGroup( Event.IniDCSGroupName ) Event.IniGroup = self:FindGroup( Event.IniDCSGroupName )
local PlayerName = Event.IniUnit:GetPlayerName() local PlayerName = Event.IniUnit:GetPlayerName()
self:E( { "PlayerName:", PlayerName } ) self:E( { "PlayerName:", PlayerName } )
if PlayerName ~= "" then if PlayerName then
self:E( { "Player Joined:", PlayerName } ) self:E( { "Player Joined:", PlayerName } )
if not self.PLAYERS[PlayerName] then if not self.PLAYERS[PlayerName] then
self:AddPlayer( Event.IniUnitName, PlayerName ) self:AddPlayer( Event.IniUnitName, PlayerName )
@@ -803,7 +900,7 @@ function DATABASE:_EventOnPlayerLeaveUnit( Event )
if Event.IniUnit then if Event.IniUnit then
if Event.IniObjectCategory == 1 then if Event.IniObjectCategory == 1 then
local PlayerName = Event.IniUnit:GetPlayerName() local PlayerName = Event.IniUnit:GetPlayerName()
if self.PLAYERS[PlayerName] then if PlayerName and self.PLAYERS[PlayerName] then
self:E( { "Player Left:", PlayerName } ) self:E( { "Player Left:", PlayerName } )
local Settings = SETTINGS:Set( PlayerName ) local Settings = SETTINGS:Set( PlayerName )
Settings:RemovePlayerMenu( Event.IniUnit ) Settings:RemovePlayerMenu( Event.IniUnit )
@@ -1025,7 +1122,6 @@ function DATABASE:_RegisterTemplates()
local CoalitionSide = coalition.side[string.upper(CoalitionName)] local CoalitionSide = coalition.side[string.upper(CoalitionName)]
----------------------------------------------
-- build nav points DB -- build nav points DB
self.Navpoints[CoalitionName] = {} self.Navpoints[CoalitionName] = {}
if coa_data.nav_points then --navpoints if coa_data.nav_points then --navpoints
@@ -1040,8 +1136,9 @@ function DATABASE:_RegisterTemplates()
self.Navpoints[CoalitionName][nav_ind]['point']['y'] = 0 self.Navpoints[CoalitionName][nav_ind]['point']['y'] = 0
self.Navpoints[CoalitionName][nav_ind]['point']['z'] = nav_data.y self.Navpoints[CoalitionName][nav_ind]['point']['z'] = nav_data.y
end end
end
end end
end
------------------------------------------------- -------------------------------------------------
if coa_data.country then --there is a country table if coa_data.country then --there is a country table
for cntry_id, cntry_data in pairs(coa_data.country) do for cntry_id, cntry_data in pairs(coa_data.country) do
@@ -1097,6 +1194,8 @@ function DATABASE:_RegisterTemplates()
for ZoneID, ZoneData in pairs( env.mission.triggers.zones ) do for ZoneID, ZoneData in pairs( env.mission.triggers.zones ) do
local ZoneName = ZoneData.name local ZoneName = ZoneData.name
self.ZONENAMES[ZoneName] = ZoneName self.ZONENAMES[ZoneName] = ZoneName
self:AddZone( ZoneName, ZONE:New( ZoneName ) )
self:I( "Added ZONE " .. ZoneName )
end end
return self return self

View File

@@ -174,6 +174,7 @@
EVENT = { EVENT = {
ClassName = "EVENT", ClassName = "EVENT",
ClassID = 0, ClassID = 0,
MissionEnd = false,
} }
world.event.S_EVENT_NEW_CARGO = world.event.S_EVENT_MAX + 1000 world.event.S_EVENT_NEW_CARGO = world.event.S_EVENT_MAX + 1000
@@ -748,8 +749,13 @@ function EVENT:onEvent( Event )
if self and if self and
self.Events and self.Events and
self.Events[Event.id] and self.Events[Event.id] and
self.MissionEnd == false and
( Event.initiator ~= nil or ( Event.initiator == nil and Event.id ~= EVENTS.PlayerLeaveUnit ) ) then ( Event.initiator ~= nil or ( Event.initiator == nil and Event.id ~= EVENTS.PlayerLeaveUnit ) ) then
if Event.id and Event.id == EVENTS.MissionEnd then
self.MissionEnd = true
end
if Event.initiator then if Event.initiator then
Event.IniObjectCategory = Event.initiator:getCategory() Event.IniObjectCategory = Event.initiator:getCategory()
@@ -788,6 +794,16 @@ function EVENT:onEvent( Event )
Event.IniTypeName = Event.IniDCSUnit:getTypeName() Event.IniTypeName = Event.IniDCSUnit:getTypeName()
end end
if Event.IniObjectCategory == Object.Category.CARGO then
Event.IniDCSUnit = Event.initiator
Event.IniDCSUnitName = Event.IniDCSUnit:getName()
Event.IniUnitName = Event.IniDCSUnitName
Event.IniUnit = CARGO:FindByName( Event.IniDCSUnitName )
Event.IniCoalition = Event.IniDCSUnit:getCoalition()
Event.IniCategory = Event.IniDCSUnit:getDesc().category
Event.IniTypeName = Event.IniDCSUnit:getTypeName()
end
if Event.IniObjectCategory == Object.Category.SCENERY then if Event.IniObjectCategory == Object.Category.SCENERY then
Event.IniDCSUnit = Event.initiator Event.IniDCSUnit = Event.initiator
Event.IniDCSUnitName = Event.IniDCSUnit:getName() Event.IniDCSUnitName = Event.IniDCSUnit:getName()
@@ -872,7 +888,7 @@ function EVENT:onEvent( Event )
-- Okay, we got the event from DCS. Now loop the SORTED self.EventSorted[] table for the received Event.id, and for each EventData registered, check if a function needs to be called. -- Okay, we got the event from DCS. Now loop the SORTED self.EventSorted[] table for the received Event.id, and for each EventData registered, check if a function needs to be called.
for EventClass, EventData in pairs( self.Events[Event.id][EventPriority] ) do for EventClass, EventData in pairs( self.Events[Event.id][EventPriority] ) do
--if Event.IniObjectCategory ~= Object.Category.STATIC then --if Event.IniObjectCategory ~= Object.Category.STATIC then
-- self:E( { "Evaluating: ", EventClass:GetClassNameAndID() } ) -- self:E( { "Evaluating: ", EventClass:GetClassNameAndID() } )
--end --end
@@ -1021,6 +1037,16 @@ function EVENT:onEvent( Event )
end end
end end
end end
-- When cargo was deleted, it may probably be because of an S_EVENT_DEAD.
-- However, in the loading logic, an S_EVENT_DEAD is also generated after a Destroy() call.
-- And this is a problem because it will remove all entries from the SET_CARGOs.
-- To prevent this from happening, the Cargo object has a flag NoDestroy.
-- When true, the SET_CARGO won't Remove the Cargo object from the set.
-- But we need to switch that flag off after the event handlers have been called.
if Event.id == EVENTS.DeleteCargo then
Event.Cargo.NoDestroy = nil
end
else else
self:T( { EventMeta.Text, Event } ) self:T( { EventMeta.Text, Event } )
end end

View File

@@ -337,7 +337,7 @@ do -- FSM
--- Creates a new FSM object. --- Creates a new FSM object.
-- @param #FSM self -- @param #FSM self
-- @return #FSM -- @return #FSM
function FSM:New( FsmT ) function FSM:New()
-- Inherits from BASE -- Inherits from BASE
self = BASE:Inherit( self, BASE:New() ) self = BASE:Inherit( self, BASE:New() )
@@ -441,6 +441,8 @@ do -- FSM
-- @return #table -- @return #table
function FSM:GetProcesses() function FSM:GetProcesses()
self:F( { Processes = self._Processes } )
return self._Processes or {} return self._Processes or {}
end end
@@ -455,6 +457,18 @@ do -- FSM
error( "Sub-Process from state " .. From .. " with event " .. Event .. " not found!" ) error( "Sub-Process from state " .. From .. " with event " .. Event .. " not found!" )
end end
function FSM:SetProcess( From, Event, Fsm )
for ProcessID, Process in pairs( self:GetProcesses() ) do
if Process.From == From and Process.Event == Event then
Process.fsm = Fsm
return true
end
end
error( "Sub-Process from state " .. From .. " with event " .. Event .. " not found!" )
end
--- Adds an End state. --- Adds an End state.
function FSM:AddEndState( State ) function FSM:AddEndState( State )
@@ -557,8 +571,9 @@ do -- FSM
end end
function FSM:_call_handler( handler, params, EventName ) function FSM:_call_handler( step, trigger, params, EventName )
local handler = step .. trigger
local ErrorHandler = function( errmsg ) local ErrorHandler = function( errmsg )
env.info( "Error in SCHEDULER function:" .. errmsg ) env.info( "Error in SCHEDULER function:" .. errmsg )
@@ -569,83 +584,117 @@ do -- FSM
return errmsg return errmsg
end end
if self[handler] then if self[handler] then
self:T2( "Calling " .. handler ) self:T( "*** FSM *** " .. step .. " *** " .. params[1] .. " --> " .. params[2] .. " --> " .. params[3] )
self._EventSchedules[EventName] = nil self._EventSchedules[EventName] = nil
local Result, Value = xpcall( function() return self[handler]( self, unpack( params ) ) end, ErrorHandler ) local Result, Value = xpcall( function() return self[handler]( self, unpack( params ) ) end, ErrorHandler )
return Value return Value
end end
end end
--- @param #FSM self
function FSM._handler( self, EventName, ... ) function FSM._handler( self, EventName, ... )
local Can, to = self:can( EventName ) local Can, To = self:can( EventName )
if to == "*" then if To == "*" then
to = self.current To = self.current
end end
if Can then if Can then
local from = self.current local From = self.current
local params = { from, EventName, to, ... } local Params = { From, EventName, To, ... }
if self.Controllable then
self:T( "FSM Transition for " .. self.Controllable.ControllableName .. " :" .. self.current .. " --> " .. EventName .. " --> " .. to ) if self["onleave".. From] or
self["OnLeave".. From] or
self["onbefore".. EventName] or
self["OnBefore".. EventName] or
self["onafter".. EventName] or
self["OnAfter".. EventName] or
self["onenter".. To] or
self["OnEnter".. To]
then
if self:_call_handler( "onbefore", EventName, Params, EventName ) == false then
self:T( "*** FSM *** Cancel" .. " *** " .. self.current .. " --> " .. EventName .. " --> " .. To .. " *** onbefore" .. EventName )
return false
else
if self:_call_handler( "OnBefore", EventName, Params, EventName ) == false then
self:T( "*** FSM *** Cancel" .. " *** " .. self.current .. " --> " .. EventName .. " --> " .. To .. " *** OnBefore" .. EventName )
return false
else
if self:_call_handler( "onleave", From, Params, EventName ) == false then
self:T( "*** FSM *** Cancel" .. " *** " .. self.current .. " --> " .. EventName .. " --> " .. To .. " *** onleave" .. From )
return false
else
if self:_call_handler( "OnLeave", From, Params, EventName ) == false then
self:T( "*** FSM *** Cancel" .. " *** " .. self.current .. " --> " .. EventName .. " --> " .. To .. " *** OnLeave" .. From )
return false
end
end
end
end
else else
self:T( "FSM Transition:" .. self.current .. " --> " .. EventName .. " --> " .. to ) local ClassName = self:GetClassName()
end if ClassName == "FSM" then
self:T( "*** FSM *** Transit *** " .. self.current .. " --> " .. EventName .. " --> " .. To )
end
if ( self:_call_handler("onbefore" .. EventName, params, EventName ) == false ) if ClassName == "FSM_TASK" then
or ( self:_call_handler("OnBefore" .. EventName, params, EventName ) == false ) self:T( "*** FSM *** Transit *** " .. self.current .. " --> " .. EventName .. " --> " .. To .. " *** Task: " .. self.TaskName )
or ( self:_call_handler("onleave" .. from, params, EventName ) == false ) end
or ( self:_call_handler("OnLeave" .. from, params, EventName ) == false ) then
self:T( "Cancel Transition" ) if ClassName == "FSM_CONTROLLABLE" then
return false self:T( "*** FSM *** Transit *** " .. self.current .. " --> " .. EventName .. " --> " .. To .. " *** TaskUnit: " .. self.Controllable.ControllableName .. " *** " )
end
if ClassName == "FSM_PROCESS" then
self:T( "*** FSM *** Transit *** " .. self.current .. " --> " .. EventName .. " --> " .. To .. " *** Task: " .. self.Task:GetName() .. ", TaskUnit: " .. self.Controllable.ControllableName .. " *** " )
end
end end
self.current = to self.current = To
local execute = true local execute = true
local subtable = self:_gosub( from, EventName ) local subtable = self:_gosub( From, EventName )
for _, sub in pairs( subtable ) do for _, sub in pairs( subtable ) do
--if sub.nextevent then --if sub.nextevent then
-- self:F2( "nextevent = " .. sub.nextevent ) -- self:F2( "nextevent = " .. sub.nextevent )
-- self[sub.nextevent]( self ) -- self[sub.nextevent]( self )
--end --end
self:T( "calling sub start event: " .. sub.StartEvent ) self:T( "*** FSM *** Sub *** " .. sub.StartEvent )
sub.fsm.fsmparent = self sub.fsm.fsmparent = self
sub.fsm.ReturnEvents = sub.ReturnEvents sub.fsm.ReturnEvents = sub.ReturnEvents
sub.fsm[sub.StartEvent]( sub.fsm ) sub.fsm[sub.StartEvent]( sub.fsm )
execute = false execute = false
end end
local fsmparent, Event = self:_isendstate( to ) local fsmparent, Event = self:_isendstate( To )
if fsmparent and Event then if fsmparent and Event then
self:F2( { "end state: ", fsmparent, Event } ) self:T( "*** FSM *** End *** " .. Event )
self:_call_handler("onenter" .. to, params, EventName ) self:_call_handler("onenter", To, Params, EventName )
self:_call_handler("OnEnter" .. to, params, EventName ) self:_call_handler("OnEnter", To, Params, EventName )
self:_call_handler("onafter" .. EventName, params, EventName ) self:_call_handler("onafter", EventName, Params, EventName )
self:_call_handler("OnAfter" .. EventName, params, EventName ) self:_call_handler("OnAfter", EventName, Params, EventName )
self:_call_handler("onstatechange", params, EventName ) self:_call_handler("onstate", "change", Params, EventName )
fsmparent[Event]( fsmparent ) fsmparent[Event]( fsmparent )
execute = false execute = false
end end
if execute then if execute then
self:_call_handler("onafter", EventName, Params, EventName )
self:_call_handler("OnAfter", EventName, Params, EventName )
-- only execute the call if the From state is not equal to the To state! Otherwise this function should never execute! -- only execute the call if the From state is not equal to the To state! Otherwise this function should never execute!
--if from ~= to then --if from ~= to then
self:_call_handler("onenter" .. to, params, EventName ) self:_call_handler("onenter", To, Params, EventName )
self:_call_handler("OnEnter" .. to, params, EventName ) self:_call_handler("OnEnter", To, Params, EventName )
--end --end
self:_call_handler("onafter" .. EventName, params, EventName ) self:_call_handler("onstate", "change", Params, EventName )
self:_call_handler("OnAfter" .. EventName, params, EventName )
self:_call_handler("onstatechange", params, EventName )
end end
else else
self:T( "Cannot execute transition." ) self:T( "*** FSM *** NO Transition *** " .. self.current .. " --> " .. EventName .. " --> ? " )
self:T( { From = self.current, Event = EventName, To = to, Can = Can } )
end end
return nil return nil
@@ -691,17 +740,16 @@ do -- FSM
function FSM:_isendstate( Current ) function FSM:_isendstate( Current )
local FSMParent = self.fsmparent local FSMParent = self.fsmparent
if FSMParent and self.endstates[Current] then if FSMParent and self.endstates[Current] then
self:T( { state = Current, endstates = self.endstates, endstate = self.endstates[Current] } ) --self:T( { state = Current, endstates = self.endstates, endstate = self.endstates[Current] } )
FSMParent.current = Current FSMParent.current = Current
local ParentFrom = FSMParent.current local ParentFrom = FSMParent.current
self:T( ParentFrom ) --self:T( { ParentFrom, self.ReturnEvents } )
self:T( self.ReturnEvents )
local Event = self.ReturnEvents[Current] local Event = self.ReturnEvents[Current]
self:T( { ParentFrom, Event, self.ReturnEvents } ) --self:T( { Event } )
if Event then if Event then
return FSMParent, Event return FSMParent, Event
else else
self:T( { "Could not find parent event name for state ", ParentFrom } ) --self:T( { "Could not find parent event name for state ", ParentFrom } )
end end
end end
@@ -724,6 +772,10 @@ do -- FSM
return self.current return self.current
end end
function FSM:GetCurrentState()
return self.current
end
function FSM:Is( State ) function FSM:Is( State )
return self.current == State return self.current == State
@@ -769,10 +821,10 @@ do -- FSM_CONTROLLABLE
-- @param #table FSMT Finite State Machine Table -- @param #table FSMT Finite State Machine Table
-- @param Wrapper.Controllable#CONTROLLABLE Controllable (optional) The CONTROLLABLE object that the FSM_CONTROLLABLE governs. -- @param Wrapper.Controllable#CONTROLLABLE Controllable (optional) The CONTROLLABLE object that the FSM_CONTROLLABLE governs.
-- @return #FSM_CONTROLLABLE -- @return #FSM_CONTROLLABLE
function FSM_CONTROLLABLE:New( FSMT, Controllable ) function FSM_CONTROLLABLE:New( Controllable )
-- Inherits from BASE -- Inherits from BASE
local self = BASE:Inherit( self, FSM:New( FSMT ) ) -- Core.Fsm#FSM_CONTROLLABLE local self = BASE:Inherit( self, FSM:New() ) -- Core.Fsm#FSM_CONTROLLABLE
if Controllable then if Controllable then
self:SetControllable( Controllable ) self:SetControllable( Controllable )
@@ -855,7 +907,9 @@ do -- FSM_CONTROLLABLE
return self.Controllable return self.Controllable
end end
function FSM_CONTROLLABLE:_call_handler( handler, params, EventName ) function FSM_CONTROLLABLE:_call_handler( step, trigger, params, EventName )
local handler = step .. trigger
local ErrorHandler = function( errmsg ) local ErrorHandler = function( errmsg )
@@ -868,7 +922,7 @@ do -- FSM_CONTROLLABLE
end end
if self[handler] then if self[handler] then
self:F3( "Calling " .. handler ) self:T( "*** FSM *** " .. step .. " *** " .. params[1] .. " --> " .. params[2] .. " --> " .. params[3] .. " *** TaskUnit: " .. self.Controllable:GetName() )
self._EventSchedules[EventName] = nil self._EventSchedules[EventName] = nil
local Result, Value = xpcall( function() return self[handler]( self, self.Controllable, unpack( params ) ) end, ErrorHandler ) local Result, Value = xpcall( function() return self[handler]( self, self.Controllable, unpack( params ) ) end, ErrorHandler )
return Value return Value
@@ -905,9 +959,9 @@ do -- FSM_PROCESS
local self = BASE:Inherit( self, FSM_CONTROLLABLE:New() ) -- Core.Fsm#FSM_PROCESS local self = BASE:Inherit( self, FSM_CONTROLLABLE:New() ) -- Core.Fsm#FSM_PROCESS
--self:F( Controllable ) --self:F( Controllable )
self:Assign( Controllable, Task ) self:Assign( Controllable, Task )
return self return self
end end
@@ -915,7 +969,9 @@ do -- FSM_PROCESS
self:T( "No Initialisation" ) self:T( "No Initialisation" )
end end
function FSM_PROCESS:_call_handler( handler, params, EventName ) function FSM_PROCESS:_call_handler( step, trigger, params, EventName )
local handler = step .. trigger
local ErrorHandler = function( errmsg ) local ErrorHandler = function( errmsg )
@@ -928,9 +984,13 @@ do -- FSM_PROCESS
end end
if self[handler] then if self[handler] then
self:F3( "Calling " .. handler ) if handler ~= "onstatechange" then
self:T( "*** FSM *** " .. step .. " *** " .. params[1] .. " --> " .. params[2] .. " --> " .. params[3] .. " *** Task: " .. self.Task:GetName() .. ", TaskUnit: " .. self.Controllable:GetName() )
end
self._EventSchedules[EventName] = nil self._EventSchedules[EventName] = nil
local Result, Value = xpcall( function() return self[handler]( self, self.Controllable, self.Task, unpack( params ) ) end, ErrorHandler ) if self.Controllable and self.Controllable:IsAlive() == true then
local Result, Value = xpcall( function() return self[handler]( self, self.Controllable, self.Task, unpack( params ) ) end, ErrorHandler )
end
return Value return Value
--return self[handler]( self, self.Controllable, unpack( params ) ) --return self[handler]( self, self.Controllable, unpack( params ) )
end end
@@ -946,7 +1006,7 @@ do -- FSM_PROCESS
local NewFsm = self:New( Controllable, Task ) -- Core.Fsm#FSM_PROCESS local NewFsm = self:New( Controllable, Task ) -- Core.Fsm#FSM_PROCESS
NewFsm:Assign( Controllable, Task ) NewFsm:Assign( Controllable, Task )
-- Polymorphic call to initialize the new FSM_PROCESS based on self FSM_PROCESS -- Polymorphic call to initialize the new FSM_PROCESS based on self FSM_PROCESS
NewFsm:Init( self ) NewFsm:Init( self )
@@ -1037,21 +1097,21 @@ do -- FSM_PROCESS
-- TODO: Need to check and fix that an FSM_PROCESS is only for a UNIT. Not for a GROUP. -- TODO: Need to check and fix that an FSM_PROCESS is only for a UNIT. Not for a GROUP.
--- Send a message of the @{Task} to the Group of the Unit. --- Send a message of the @{Task} to the Group of the Unit.
-- @param #FSM_PROCESS self -- @param #FSM_PROCESS self
function FSM_PROCESS:Message( Message ) function FSM_PROCESS:Message( Message )
self:F( { Message = Message } ) self:F( { Message = Message } )
local CC = self:GetCommandCenter()
local TaskGroup = self.Controllable:GetGroup()
local PlayerName = self.Controllable:GetPlayerName() -- Only for a unit local CC = self:GetCommandCenter()
PlayerName = PlayerName and " (" .. PlayerName .. ")" or "" -- If PlayerName is nil, then keep it nil, otherwise add brackets. local TaskGroup = self.Controllable:GetGroup()
local Callsign = self.Controllable:GetCallsign()
local Prefix = Callsign and " @ " .. Callsign .. PlayerName or "" local PlayerName = self.Controllable:GetPlayerName() -- Only for a unit
PlayerName = PlayerName and " (" .. PlayerName .. ")" or "" -- If PlayerName is nil, then keep it nil, otherwise add brackets.
Message = Prefix .. ": " .. Message local Callsign = self.Controllable:GetCallsign()
CC:MessageToGroup( Message, TaskGroup ) local Prefix = Callsign and " @ " .. Callsign .. PlayerName or ""
end
Message = Prefix .. ": " .. Message
CC:MessageToGroup( Message, TaskGroup )
end
@@ -1072,14 +1132,16 @@ end
return self return self
end end
function FSM_PROCESS:onenterAssigned( ProcessUnit ) -- function FSM_PROCESS:onenterAssigned( ProcessUnit, Task, From, Event, To )
self:T( "Assign" ) --
-- if From( "Planned" ) then
-- self:T( "*** FSM *** Assign *** " .. Task:GetName() .. "/" .. ProcessUnit:GetName() .. " *** " .. From .. " --> " .. Event .. " --> " .. To )
-- self.Task:Assign()
-- end
-- end
self.Task:Assign() function FSM_PROCESS:onenterFailed( ProcessUnit, Task, From, Event, To )
end self:T( "*** FSM *** Failed *** " .. Task:GetName() .. "/" .. ProcessUnit:GetName() .. " *** " .. From .. " --> " .. Event .. " --> " .. To )
function FSM_PROCESS:onenterFailed( ProcessUnit )
self:T( "Failed" )
self.Task:Fail() self.Task:Fail()
end end
@@ -1091,14 +1153,17 @@ end
-- @param #string Event -- @param #string Event
-- @param #string From -- @param #string From
-- @param #string To -- @param #string To
function FSM_PROCESS:onstatechange( ProcessUnit, Task, From, Event, To, Dummy ) function FSM_PROCESS:onstatechange( ProcessUnit, Task, From, Event, To )
self:T( { ProcessUnit:GetName(), From, Event, To, Dummy, self:IsTrace() } )
if self:IsTrace() then if From ~= To then
--MESSAGE:New( "@ Process " .. self:GetClassNameAndID() .. " : " .. Event .. " changed to state " .. To, 2 ):ToAll() self:T( "*** FSM *** Change *** " .. Task:GetName() .. "/" .. ProcessUnit:GetName() .. " *** " .. From .. " --> " .. Event .. " --> " .. To )
end end
self:T( { Scores = self._Scores, To = To } ) -- if self:IsTrace() then
-- MESSAGE:New( "@ Process " .. self:GetClassNameAndID() .. " : " .. Event .. " changed to state " .. To, 2 ):ToAll()
-- self:F2( { Scores = self._Scores, To = To } )
-- end
-- TODO: This needs to be reworked with a callback functions allocated within Task, and set within the mission script from the Task Objects... -- TODO: This needs to be reworked with a callback functions allocated within Task, and set within the mission script from the Task Objects...
if self._Scores[To] then if self._Scores[To] then
@@ -1133,22 +1198,23 @@ do -- FSM_TASK
--- Creates a new FSM_TASK object. --- Creates a new FSM_TASK object.
-- @param #FSM_TASK self -- @param #FSM_TASK self
-- @param #table FSMT -- @param #string TaskName The name of the task.
-- @param Tasking.Task#TASK Task
-- @param Wrapper.Unit#UNIT TaskUnit
-- @return #FSM_TASK -- @return #FSM_TASK
function FSM_TASK:New( FSMT ) function FSM_TASK:New( TaskName )
local self = BASE:Inherit( self, FSM_CONTROLLABLE:New( FSMT ) ) -- Core.Fsm#FSM_TASK local self = BASE:Inherit( self, FSM_CONTROLLABLE:New() ) -- Core.Fsm#FSM_TASK
self["onstatechange"] = self.OnStateChange self["onstatechange"] = self.OnStateChange
self.TaskName = TaskName
return self return self
end end
function FSM_TASK:_call_handler( handler, params, EventName ) function FSM_TASK:_call_handler( step, trigger, params, EventName )
local handler = step .. trigger
if self[handler] then if self[handler] then
self:T( "Calling " .. handler ) self:T( "*** FSM *** " .. step .. " *** " .. params[1] .. " --> " .. params[2] .. " --> " .. params[3] .. " *** Task: " .. self.TaskName )
self._EventSchedules[EventName] = nil self._EventSchedules[EventName] = nil
return self[handler]( self, unpack( params ) ) return self[handler]( self, unpack( params ) )
end end
@@ -1210,9 +1276,10 @@ do -- FSM_SET
return self.Controllable return self.Controllable
end end
function FSM_SET:_call_handler( handler, params, EventName ) function FSM_SET:_call_handler( step, trigger, params, EventName )
local handler = step .. trigger
if self[handler] then if self[handler] then
self:T( "Calling " .. handler ) self:T( "*** FSM *** " .. step .. " *** " .. params[1] .. " --> " .. params[2] .. " --> " .. params[3] )
self._EventSchedules[EventName] = nil self._EventSchedules[EventName] = nil
return self[handler]( self, self.Set, unpack( params ) ) return self[handler]( self, self.Set, unpack( params ) )
end end

View File

@@ -213,6 +213,7 @@ do -- MENU_BASE
self.Menus = {} self.Menus = {}
self.MenuCount = 0 self.MenuCount = 0
self.MenuTime = timer.getTime() self.MenuTime = timer.getTime()
self.MenuRemoveParent = false
if self.ParentMenu then if self.ParentMenu then
self.ParentMenu.Menus = self.ParentMenu.Menus or {} self.ParentMenu.Menus = self.ParentMenu.Menus or {}
@@ -226,14 +227,30 @@ do -- MENU_BASE
if self.ParentMenu then if self.ParentMenu then
self.ParentMenu.Menus = self.ParentMenu.Menus or {} self.ParentMenu.Menus = self.ParentMenu.Menus or {}
self.ParentMenu.Menus[MenuText] = Menu self.ParentMenu.Menus[MenuText] = Menu
self.ParentMenu.MenuCount = self.ParentMenu.MenuCount + 1
end end
end end
function MENU_BASE:ClearParentMenu( MenuText ) function MENU_BASE:ClearParentMenu( MenuText )
if self.ParentMenu and self.ParentMenu.Menus[MenuText] then if self.ParentMenu and self.ParentMenu.Menus[MenuText] then
self.ParentMenu.Menus[MenuText] = nil self.ParentMenu.Menus[MenuText] = nil
self.ParentMenu.MenuCount = self.ParentMenu.MenuCount - 1
if self.ParentMenu.MenuCount == 0 then
--self.ParentMenu:Remove()
end
end end
end end
--- Sets a @{Menu} to remove automatically the parent menu when the menu removed is the last child menu of that parent @{Menu}.
-- @param #MENU_BASE self
-- @param #boolean RemoveParent If true, the parent menu is automatically removed when this menu is the last child menu of that parent @{Menu}.
-- @return #MENU_BASE
function MENU_BASE:SetRemoveParent( RemoveParent )
--self:F( { RemoveParent } )
self.MenuRemoveParent = RemoveParent
return self
end
--- Gets a @{Menu} from a parent @{Menu} --- Gets a @{Menu} from a parent @{Menu}
-- @param #MENU_BASE self -- @param #MENU_BASE self
@@ -900,8 +917,8 @@ do
self:RemoveSubMenus( MenuTime, MenuTag ) self:RemoveSubMenus( MenuTime, MenuTag )
if not MenuTime or self.MenuTime ~= MenuTime then if not MenuTime or self.MenuTime ~= MenuTime then
if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then
self:F( { Group = self.GroupID, Text = self.MenuText, Path = self.MenuPath } )
if self.MenuPath ~= nil then if self.MenuPath ~= nil then
self:F( { Group = self.GroupID, Text = self.MenuText, Path = self.MenuPath } )
missionCommands.removeItemForGroup( self.GroupID, self.MenuPath ) missionCommands.removeItemForGroup( self.GroupID, self.MenuPath )
end end
MENU_INDEX:ClearGroupMenu( self.Group, Path ) MENU_INDEX:ClearGroupMenu( self.Group, Path )
@@ -992,8 +1009,8 @@ do
if GroupMenu == self then if GroupMenu == self then
if not MenuTime or self.MenuTime ~= MenuTime then if not MenuTime or self.MenuTime ~= MenuTime then
if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then
self:F( { Group = self.GroupID, Text = self.MenuText, Path = self.MenuPath } )
if self.MenuPath ~= nil then if self.MenuPath ~= nil then
self:F( { Group = self.GroupID, Text = self.MenuText, Path = self.MenuPath } )
missionCommands.removeItemForGroup( self.GroupID, self.MenuPath ) missionCommands.removeItemForGroup( self.GroupID, self.MenuPath )
end end
MENU_INDEX:ClearGroupMenu( self.Group, Path ) MENU_INDEX:ClearGroupMenu( self.Group, Path )
@@ -1133,8 +1150,8 @@ do
self:RemoveSubMenus( MenuTime, MenuTag ) self:RemoveSubMenus( MenuTime, MenuTag )
if not MenuTime or self.MenuTime ~= MenuTime then if not MenuTime or self.MenuTime ~= MenuTime then
if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then
self:F( { Group = self.GroupID, Text = self.MenuText, Path = self.MenuPath } )
if self.MenuPath ~= nil then if self.MenuPath ~= nil then
self:F( { Group = self.GroupID, Text = self.MenuText, Path = self.MenuPath } )
missionCommands.removeItemForGroup( self.GroupID, self.MenuPath ) missionCommands.removeItemForGroup( self.GroupID, self.MenuPath )
end end
MENU_INDEX:ClearGroupMenu( self.Group, Path ) MENU_INDEX:ClearGroupMenu( self.Group, Path )
@@ -1244,8 +1261,8 @@ do
if GroupMenu == self then if GroupMenu == self then
if not MenuTime or self.MenuTime ~= MenuTime then if not MenuTime or self.MenuTime ~= MenuTime then
if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then
self:F( { Group = self.GroupID, Text = self.MenuText, Path = self.MenuPath } )
if self.MenuPath ~= nil then if self.MenuPath ~= nil then
self:F( { Group = self.GroupID, Text = self.MenuText, Path = self.MenuPath } )
missionCommands.removeItemForGroup( self.GroupID, self.MenuPath ) missionCommands.removeItemForGroup( self.GroupID, self.MenuPath )
end end
MENU_INDEX:ClearGroupMenu( self.Group, Path ) MENU_INDEX:ClearGroupMenu( self.Group, Path )

View File

@@ -192,6 +192,20 @@ do -- COORDINATE
return self return self
end end
--- COORDINATE constructor.
-- @param #COORDINATE self
-- @param #COORDINATE Coordinate.
-- @return #COORDINATE
function COORDINATE:NewFromCoordinate( Coordinate )
local self = BASE:Inherit( self, BASE:New() ) -- #COORDINATE
self.x = Coordinate.x
self.y = Coordinate.y
self.z = Coordinate.z
return self
end
--- Create a new COORDINATE object from Vec2 coordinates. --- Create a new COORDINATE object from Vec2 coordinates.
-- @param #COORDINATE self -- @param #COORDINATE self
-- @param Dcs.DCSTypes#Vec2 Vec2 The Vec2 point. -- @param Dcs.DCSTypes#Vec2 Vec2 The Vec2 point.
@@ -241,21 +255,38 @@ do -- COORDINATE
return { x = self.x, y = self.z } return { x = self.x, y = self.z }
end end
--TODO: check this to replace
--- Calculate the distance from a reference @{DCSTypes#Vec2}. --- Returns if the 2 coordinates are at the same 2D position.
-- @param #COORDINATE self -- @param #COORDINATE self
-- @param Dcs.DCSTypes#Vec2 Vec2Reference The reference @{DCSTypes#Vec2}. -- @param #COORDINATE Coordinate
-- @return Dcs.DCSTypes#Distance The distance from the reference @{DCSTypes#Vec2} in meters. -- @param #number Precision
function COORDINATE:DistanceFromVec2( Vec2Reference ) -- @return #boolean true if at the same position.
self:F2( Vec2Reference ) function COORDINATE:IsAtCoordinate2D( Coordinate, Precision )
self:F( { Coordinate = Coordinate:GetVec2() } )
self:F( { self = self:GetVec2() } )
local x = Coordinate.x
local z = Coordinate.z
return x - Precision <= self.x and x + Precision >= self.x and z - Precision <= self.z and z + Precision >= self.z
end
local Distance = ( ( Vec2Reference.x - self.x ) ^ 2 + ( Vec2Reference.y - self.z ) ^2 ) ^0.5 --- Calculate the distance from a reference @{#COORDINATE}.
-- @param #COORDINATE self
-- @param #COORDINATE PointVec2Reference The reference @{#COORDINATE}.
-- @return Dcs.DCSTypes#Distance The distance from the reference @{#COORDINATE} in meters.
function COORDINATE:DistanceFromPointVec2( PointVec2Reference )
self:F2( PointVec2Reference )
local Distance = ( ( PointVec2Reference.x - self.x ) ^ 2 + ( PointVec2Reference.z - self.z ) ^2 ) ^ 0.5
self:T2( Distance ) self:T2( Distance )
return Distance return Distance
end end
--- Add a Distance in meters from the COORDINATE orthonormal plane, with the given angle, and calculate the new COORDINATE. --- Add a Distance in meters from the COORDINATE orthonormal plane, with the given angle, and calculate the new COORDINATE.
-- @param #COORDINATE self -- @param #COORDINATE self
-- @param Dcs.DCSTypes#Distance Distance The Distance to be added in meters. -- @param Dcs.DCSTypes#Distance Distance The Distance to be added in meters.
@@ -303,6 +334,18 @@ do -- COORDINATE
end end
--- Return a random Coordinate within an Outer Radius and optionally NOT within an Inner Radius of the COORDINATE.
-- @param #COORDINATE self
-- @param Dcs.DCSTypes#Distance OuterRadius
-- @param Dcs.DCSTypes#Distance InnerRadius
-- @return #COORDINATE
function COORDINATE:GetRandomCoordinateInRadius( OuterRadius, InnerRadius )
self:F2( { OuterRadius, InnerRadius } )
return COORDINATE:NewFromVec2( self:GetRandomVec2InRadius( OuterRadius, InnerRadius ) )
end
--- Return a random Vec3 within an Outer Radius and optionally NOT within an Inner Radius of the COORDINATE. --- Return a random Vec3 within an Outer Radius and optionally NOT within an Inner Radius of the COORDINATE.
-- @param #COORDINATE self -- @param #COORDINATE self
-- @param Dcs.DCSTypes#Distance OuterRadius -- @param Dcs.DCSTypes#Distance OuterRadius
@@ -426,8 +469,8 @@ do -- COORDINATE
-- @param height (Optional) parameter specifying the height ASL. -- @param height (Optional) parameter specifying the height ASL.
-- @return Temperature in Degrees Celsius. -- @return Temperature in Degrees Celsius.
function COORDINATE:GetTemperature(height) function COORDINATE:GetTemperature(height)
self:F2(height)
local y=height or self.y local y=height or self.y
env.info("FF height = "..y)
local point={x=self.x, y=height or self.y, z=self.z} local point={x=self.x, y=height or self.y, z=self.z}
-- get temperature [K] and pressure [Pa] at point -- get temperature [K] and pressure [Pa] at point
local T,P=atmosphere.getTemperatureAndPressure(point) local T,P=atmosphere.getTemperatureAndPressure(point)
@@ -850,11 +893,13 @@ do -- COORDINATE
function COORDINATE:WaypointGround( Speed, Formation ) function COORDINATE:WaypointGround( Speed, Formation )
self:F2( { Formation, Speed } ) self:F2( { Formation, Speed } )
local RoutePoint = {} local RoutePoint = {}
RoutePoint.x = self.x RoutePoint.x = self.x
RoutePoint.y = self.z RoutePoint.y = self.z
RoutePoint.action = Formation or "" RoutePoint.action = Formation or ""
--RoutePoint.formation_template = Formation and "" or nil
RoutePoint.speed = ( Speed or 20 ) / 3.6 RoutePoint.speed = ( Speed or 20 ) / 3.6
@@ -897,11 +942,15 @@ do -- COORDINATE
function COORDINATE:GetPathOnRoad(ToCoord) function COORDINATE:GetPathOnRoad(ToCoord)
local Path={} local Path={}
local path = land.findPathOnRoads("roads", self.x, self.z, ToCoord.x, ToCoord.z) local path = land.findPathOnRoads("roads", self.x, self.z, ToCoord.x, ToCoord.z)
for i, v in ipairs(path) do Path[#Path+1]=COORDINATE:NewFromVec2(path[1])
--self:E(v) Path[#Path+1]=COORDINATE:NewFromVec2(path[#path])
local coord=COORDINATE:NewFromVec2(v) -- I've removed this stuff because it severely slows down DCS in case of paths with a lot of segments.
Path[#Path+1]=COORDINATE:NewFromVec2(v) -- Just the beginning and the end point is sufficient.
end -- for i, v in ipairs(path) do
-- self:I(v)
-- local coord=COORDINATE:NewFromVec2(v)
-- Path[#Path+1]=COORDINATE:NewFromVec2(v)
-- end
return Path return Path
end end
@@ -964,6 +1013,88 @@ do -- COORDINATE
self:Smoke( SMOKECOLOR.Blue ) self:Smoke( SMOKECOLOR.Blue )
end end
--- Big smoke and fire at the coordinate.
-- @param #COORDINATE self
-- @param Utilities.Utils#BIGSMOKEPRESET preset Smoke preset (0=small smoke and fire, 1=medium smoke and fire, 2=large smoke and fire, 3=huge smoke and fire, 4=small smoke, 5=medium smoke, 6=large smoke, 7=huge smoke).
-- @param #number density (Optional) Smoke density. Number in [0,...,1]. Default 0.5.
function COORDINATE:BigSmokeAndFire( preset, density )
self:F2( { preset=preset, density=density } )
density=density or 0.5
trigger.action.effectSmokeBig( self:GetVec3(), preset, density )
end
--- Small smoke and fire at the coordinate.
-- @param #COORDINATE self
-- @number density (Optional) Smoke density. Number between 0 and 1. Default 0.5.
function COORDINATE:BigSmokeAndFireSmall( density )
self:F2( { density=density } )
density=density or 0.5
self:BigSmokeAndFire(BIGSMOKEPRESET.SmallSmokeAndFire, density)
end
--- Medium smoke and fire at the coordinate.
-- @param #COORDINATE self
-- @number density (Optional) Smoke density. Number between 0 and 1. Default 0.5.
function COORDINATE:BigSmokeAndFireMedium( density )
self:F2( { density=density } )
density=density or 0.5
self:BigSmokeAndFire(BIGSMOKEPRESET.MediumSmokeAndFire, density)
end
--- Large smoke and fire at the coordinate.
-- @param #COORDINATE self
-- @number density (Optional) Smoke density. Number between 0 and 1. Default 0.5.
function COORDINATE:BigSmokeAndFireLarge( density )
self:F2( { density=density } )
density=density or 0.5
self:BigSmokeAndFire(BIGSMOKEPRESET.LargeSmokeAndFire, density)
end
--- Huge smoke and fire at the coordinate.
-- @param #COORDINATE self
-- @number density (Optional) Smoke density. Number between 0 and 1. Default 0.5.
function COORDINATE:BigSmokeAndFireHuge( density )
self:F2( { density=density } )
density=density or 0.5
self:BigSmokeAndFire(BIGSMOKEPRESET.HugeSmokeAndFire, density)
end
--- Small smoke at the coordinate.
-- @param #COORDINATE self
-- @number density (Optional) Smoke density. Number between 0 and 1. Default 0.5.
function COORDINATE:BigSmokeSmall( density )
self:F2( { density=density } )
density=density or 0.5
self:BigSmokeAndFire(BIGSMOKEPRESET.SmallSmoke, density)
end
--- Medium smoke at the coordinate.
-- @param #COORDINATE self
-- @number density (Optional) Smoke density. Number between 0 and 1. Default 0.5.
function COORDINATE:BigSmokeMedium( density )
self:F2( { density=density } )
density=density or 0.5
self:BigSmokeAndFire(BIGSMOKEPRESET.MediumSmoke, density)
end
--- Large smoke at the coordinate.
-- @param #COORDINATE self
-- @number density (Optional) Smoke density. Number between 0 and 1. Default 0.5.
function COORDINATE:BigSmokeLarge( density )
self:F2( { density=density } )
density=density or 0.5
self:BigSmokeAndFire(BIGSMOKEPRESET.LargeSmoke, density)
end
--- Huge smoke at the coordinate.
-- @param #COORDINATE self
-- @number density (Optional) Smoke density. Number between 0 and 1. Default 0.5.
function COORDINATE:BigSmokeHuge( density )
self:F2( { density=density } )
density=density or 0.5
self:BigSmokeAndFire(BIGSMOKEPRESET.HugeSmoke, density)
end
--- Flares the point in a color. --- Flares the point in a color.
-- @param #COORDINATE self -- @param #COORDINATE self
-- @param Utilities.Utils#FLARECOLOR FlareColor -- @param Utilities.Utils#FLARECOLOR FlareColor
@@ -1244,7 +1375,7 @@ do -- COORDINATE
-- @return #string The coordinate Text in the configured coordinate system. -- @return #string The coordinate Text in the configured coordinate system.
function COORDINATE:ToStringFromRP( ReferenceCoord, ReferenceName, Controllable, Settings ) -- R2.2 function COORDINATE:ToStringFromRP( ReferenceCoord, ReferenceName, Controllable, Settings ) -- R2.2
self:F( { ReferenceCoord = ReferenceCoord, ReferenceName = ReferenceName } ) self:F2( { ReferenceCoord = ReferenceCoord, ReferenceName = ReferenceName } )
local Settings = Settings or ( Controllable and _DATABASE:GetPlayerSettings( Controllable:GetPlayerName() ) ) or _SETTINGS local Settings = Settings or ( Controllable and _DATABASE:GetPlayerSettings( Controllable:GetPlayerName() ) ) or _SETTINGS
@@ -1273,7 +1404,7 @@ do -- COORDINATE
-- @return #string The coordinate Text in the configured coordinate system. -- @return #string The coordinate Text in the configured coordinate system.
function COORDINATE:ToStringA2G( Controllable, Settings ) -- R2.2 function COORDINATE:ToStringA2G( Controllable, Settings ) -- R2.2
self:F( { Controllable = Controllable and Controllable:GetName() } ) self:F2( { Controllable = Controllable and Controllable:GetName() } )
local Settings = Settings or ( Controllable and _DATABASE:GetPlayerSettings( Controllable:GetPlayerName() ) ) or _SETTINGS local Settings = Settings or ( Controllable and _DATABASE:GetPlayerSettings( Controllable:GetPlayerName() ) ) or _SETTINGS
@@ -1308,7 +1439,7 @@ do -- COORDINATE
-- @return #string The coordinate Text in the configured coordinate system. -- @return #string The coordinate Text in the configured coordinate system.
function COORDINATE:ToStringA2A( Controllable, Settings ) -- R2.2 function COORDINATE:ToStringA2A( Controllable, Settings ) -- R2.2
self:F( { Controllable = Controllable and Controllable:GetName() } ) self:F2( { Controllable = Controllable and Controllable:GetName() } )
local Settings = Settings or ( Controllable and _DATABASE:GetPlayerSettings( Controllable:GetPlayerName() ) ) or _SETTINGS local Settings = Settings or ( Controllable and _DATABASE:GetPlayerSettings( Controllable:GetPlayerName() ) ) or _SETTINGS
@@ -1348,7 +1479,7 @@ do -- COORDINATE
-- @return #string The coordinate Text in the configured coordinate system. -- @return #string The coordinate Text in the configured coordinate system.
function COORDINATE:ToString( Controllable, Settings, Task ) -- R2.2 function COORDINATE:ToString( Controllable, Settings, Task ) -- R2.2
self:F( { Controllable = Controllable and Controllable:GetName() } ) self:F2( { Controllable = Controllable and Controllable:GetName() } )
local Settings = Settings or ( Controllable and _DATABASE:GetPlayerSettings( Controllable:GetPlayerName() ) ) or _SETTINGS local Settings = Settings or ( Controllable and _DATABASE:GetPlayerSettings( Controllable:GetPlayerName() ) ) or _SETTINGS
@@ -1397,7 +1528,7 @@ do -- COORDINATE
-- @return #string The pressure text in the configured measurement system. -- @return #string The pressure text in the configured measurement system.
function COORDINATE:ToStringPressure( Controllable, Settings ) -- R2.3 function COORDINATE:ToStringPressure( Controllable, Settings ) -- R2.3
self:F( { Controllable = Controllable and Controllable:GetName() } ) self:F2( { Controllable = Controllable and Controllable:GetName() } )
local Settings = Settings or ( Controllable and _DATABASE:GetPlayerSettings( Controllable:GetPlayerName() ) ) or _SETTINGS local Settings = Settings or ( Controllable and _DATABASE:GetPlayerSettings( Controllable:GetPlayerName() ) ) or _SETTINGS
@@ -1413,7 +1544,7 @@ do -- COORDINATE
-- @return #string The wind text in the configured measurement system. -- @return #string The wind text in the configured measurement system.
function COORDINATE:ToStringWind( Controllable, Settings ) -- R2.3 function COORDINATE:ToStringWind( Controllable, Settings ) -- R2.3
self:F( { Controllable = Controllable and Controllable:GetName() } ) self:F2( { Controllable = Controllable and Controllable:GetName() } )
local Settings = Settings or ( Controllable and _DATABASE:GetPlayerSettings( Controllable:GetPlayerName() ) ) or _SETTINGS local Settings = Settings or ( Controllable and _DATABASE:GetPlayerSettings( Controllable:GetPlayerName() ) ) or _SETTINGS
@@ -1429,7 +1560,7 @@ do -- COORDINATE
-- @return #string The temperature text in the configured measurement system. -- @return #string The temperature text in the configured measurement system.
function COORDINATE:ToStringTemperature( Controllable, Settings ) -- R2.3 function COORDINATE:ToStringTemperature( Controllable, Settings ) -- R2.3
self:F( { Controllable = Controllable and Controllable:GetName() } ) self:F2( { Controllable = Controllable and Controllable:GetName() } )
local Settings = Settings or ( Controllable and _DATABASE:GetPlayerSettings( Controllable:GetPlayerName() ) ) or _SETTINGS local Settings = Settings or ( Controllable and _DATABASE:GetPlayerSettings( Controllable:GetPlayerName() ) ) or _SETTINGS

View File

@@ -149,6 +149,7 @@ end
-- @param #SET_BASE self -- @param #SET_BASE self
-- @param #string ObjectName -- @param #string ObjectName
function SET_BASE:Remove( ObjectName ) function SET_BASE:Remove( ObjectName )
self:F2( { ObjectName = ObjectName } )
local Object = self.Set[ObjectName] local Object = self.Set[ObjectName]
@@ -157,7 +158,6 @@ function SET_BASE:Remove( ObjectName )
if Key == ObjectName then if Key == ObjectName then
table.remove( self.Index, Index ) table.remove( self.Index, Index )
self.Set[ObjectName] = nil self.Set[ObjectName] = nil
self:Flush(self)
break break
end end
end end
@@ -173,7 +173,7 @@ end
-- @param Core.Base#BASE Object -- @param Core.Base#BASE Object
-- @return Core.Base#BASE The added BASE Object. -- @return Core.Base#BASE The added BASE Object.
function SET_BASE:Add( ObjectName, Object ) function SET_BASE:Add( ObjectName, Object )
self:F3( { ObjectName = ObjectName, Object = Object } ) self:F2( { ObjectName = ObjectName, Object = Object } )
-- Ensure that the existing element is removed from the Set before a new one is inserted to the Set -- Ensure that the existing element is removed from the Set before a new one is inserted to the Set
if self.Set[ObjectName] then if self.Set[ObjectName] then
@@ -313,10 +313,6 @@ function SET_BASE:_FilterStart()
end end
end end
self:HandleEvent( EVENTS.Birth, self._EventOnBirth )
self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash )
self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash )
-- Follow alive players and clients -- Follow alive players and clients
--self:HandleEvent( EVENTS.PlayerEnterUnit, self._EventOnPlayerEnterUnit ) --self:HandleEvent( EVENTS.PlayerEnterUnit, self._EventOnPlayerEnterUnit )
--self:HandleEvent( EVENTS.PlayerLeaveUnit, self._EventOnPlayerLeaveUnit ) --self:HandleEvent( EVENTS.PlayerLeaveUnit, self._EventOnPlayerLeaveUnit )
@@ -935,6 +931,9 @@ function SET_GROUP:FilterStart()
if _DATABASE then if _DATABASE then
self:_FilterStart() self:_FilterStart()
self:HandleEvent( EVENTS.Birth, self._EventOnBirth )
self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash )
self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash )
end end
@@ -1643,6 +1642,9 @@ do -- SET_UNIT
if _DATABASE then if _DATABASE then
self:_FilterStart() self:_FilterStart()
self:HandleEvent( EVENTS.Birth, self._EventOnBirth )
self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash )
self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash )
end end
return self return self
@@ -2559,6 +2561,9 @@ do -- SET_STATIC
if _DATABASE then if _DATABASE then
self:_FilterStart() self:_FilterStart()
self:HandleEvent( EVENTS.Birth, self._EventOnBirth )
self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash )
self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash )
end end
return self return self
@@ -3200,6 +3205,9 @@ function SET_CLIENT:FilterStart()
if _DATABASE then if _DATABASE then
self:_FilterStart() self:_FilterStart()
self:HandleEvent( EVENTS.Birth, self._EventOnBirth )
self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash )
self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash )
end end
return self return self
@@ -3599,6 +3607,9 @@ function SET_PLAYER:FilterStart()
if _DATABASE then if _DATABASE then
self:_FilterStart() self:_FilterStart()
self:HandleEvent( EVENTS.Birth, self._EventOnBirth )
self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash )
self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash )
end end
return self return self
@@ -4131,6 +4142,19 @@ function SET_CARGO:New() --R2.1
return self return self
end end
--- (R2.1) Add CARGO to SET_CARGO.
-- @param Core.Set#SET_CARGO self
-- @param Cargo.Cargo#CARGO Cargo A single cargo.
-- @return self
function SET_CARGO:AddCargo( Cargo ) --R2.4
self:Add( Cargo:GetName(), Cargo )
return self
end
--- (R2.1) Add CARGOs to SET_CARGO. --- (R2.1) Add CARGOs to SET_CARGO.
-- @param Core.Set#SET_CARGO self -- @param Core.Set#SET_CARGO self
-- @param #string AddCargoNames A single name or an array of CARGO names. -- @param #string AddCargoNames A single name or an array of CARGO names.
@@ -4257,10 +4281,9 @@ function SET_CARGO:FilterStart() --R2.1
if _DATABASE then if _DATABASE then
self:_FilterStart() self:_FilterStart()
self:HandleEvent( EVENTS.NewCargo )
self:HandleEvent( EVENTS.DeleteCargo )
end end
self:HandleEvent( EVENTS.NewCargo )
self:HandleEvent( EVENTS.DeleteCargo )
return self return self
end end
@@ -4313,6 +4336,71 @@ function SET_CARGO:FindNearestCargoFromPointVec2( PointVec2 ) --R2.1
return NearestCargo return NearestCargo
end end
function SET_CARGO:FirstCargoWithState( State )
local FirstCargo = nil
for CargoName, Cargo in pairs( self.Set ) do
if Cargo:Is( State ) then
FirstCargo = Cargo
break
end
end
return FirstCargo
end
function SET_CARGO:FirstCargoWithStateAndNotDeployed( State )
local FirstCargo = nil
for CargoName, Cargo in pairs( self.Set ) do
if Cargo:Is( State ) and not Cargo:IsDeployed() then
FirstCargo = Cargo
break
end
end
return FirstCargo
end
--- Iterate the SET_CARGO while identifying the first @{Cargo#CARGO} that is UnLoaded.
-- @param #SET_CARGO self
-- @return Cargo.Cargo#CARGO The first @{Cargo#CARGO}.
function SET_CARGO:FirstCargoUnLoaded()
local FirstCargo = self:FirstCargoWithState( "UnLoaded" )
return FirstCargo
end
--- Iterate the SET_CARGO while identifying the first @{Cargo#CARGO} that is UnLoaded and not Deployed.
-- @param #SET_CARGO self
-- @return Cargo.Cargo#CARGO The first @{Cargo#CARGO}.
function SET_CARGO:FirstCargoUnLoadedAndNotDeployed()
local FirstCargo = self:FirstCargoWithStateAndNotDeployed( "UnLoaded" )
return FirstCargo
end
--- Iterate the SET_CARGO while identifying the first @{Cargo#CARGO} that is Loaded.
-- @param #SET_CARGO self
-- @return Cargo.Cargo#CARGO The first @{Cargo#CARGO}.
function SET_CARGO:FirstCargoLoaded()
local FirstCargo = self:FirstCargoWithState( "Loaded" )
return FirstCargo
end
--- Iterate the SET_CARGO while identifying the first @{Cargo#CARGO} that is Deployed.
-- @param #SET_CARGO self
-- @return Cargo.Cargo#CARGO The first @{Cargo#CARGO}.
function SET_CARGO:FirstCargoDeployed()
local FirstCargo = self:FirstCargoWithState( "Deployed" )
return FirstCargo
end
--- (R2.1) --- (R2.1)
@@ -4336,7 +4424,7 @@ function SET_CARGO:IsIncludeObject( MCargo ) --R2.1
MCargoCoalition = true MCargoCoalition = true
end end
end end
self:T( { "Evaluated Coalition", MCargoCoalition } ) self:F( { "Evaluated Coalition", MCargoCoalition } )
MCargoInclude = MCargoInclude and MCargoCoalition MCargoInclude = MCargoInclude and MCargoCoalition
end end
@@ -4348,7 +4436,7 @@ function SET_CARGO:IsIncludeObject( MCargo ) --R2.1
MCargoType = true MCargoType = true
end end
end end
self:T( { "Evaluated Type", MCargoType } ) self:F( { "Evaluated Type", MCargoType } )
MCargoInclude = MCargoInclude and MCargoType MCargoInclude = MCargoInclude and MCargoType
end end
@@ -4360,7 +4448,7 @@ function SET_CARGO:IsIncludeObject( MCargo ) --R2.1
MCargoPrefix = true MCargoPrefix = true
end end
end end
self:T( { "Evaluated Prefix", MCargoPrefix } ) self:F( { "Evaluated Prefix", MCargoPrefix } )
MCargoInclude = MCargoInclude and MCargoPrefix MCargoInclude = MCargoInclude and MCargoPrefix
end end
end end
@@ -4374,6 +4462,8 @@ end
-- @param Core.Event#EVENTDATA EventData -- @param Core.Event#EVENTDATA EventData
function SET_CARGO:OnEventNewCargo( EventData ) --R2.1 function SET_CARGO:OnEventNewCargo( EventData ) --R2.1
self:F( { "New Cargo", EventData } )
if EventData.Cargo then if EventData.Cargo then
if EventData.Cargo and self:IsIncludeObject( EventData.Cargo ) then if EventData.Cargo and self:IsIncludeObject( EventData.Cargo ) then
self:Add( EventData.Cargo.Name , EventData.Cargo ) self:Add( EventData.Cargo.Name , EventData.Cargo )
@@ -4390,8 +4480,249 @@ function SET_CARGO:OnEventDeleteCargo( EventData ) --R2.1
if EventData.Cargo then if EventData.Cargo then
local Cargo = _DATABASE:FindCargo( EventData.Cargo.Name ) local Cargo = _DATABASE:FindCargo( EventData.Cargo.Name )
if Cargo and Cargo.Name then if Cargo and Cargo.Name then
self:Remove( Cargo.Name )
-- When cargo was deleted, it may probably be because of an S_EVENT_DEAD.
-- However, in the loading logic, an S_EVENT_DEAD is also generated after a Destroy() call.
-- And this is a problem because it will remove all entries from the SET_CARGOs.
-- To prevent this from happening, the Cargo object has a flag NoDestroy.
-- When true, the SET_CARGO won't Remove the Cargo object from the set.
-- This flag is switched off after the event handlers have been called in the EVENT class.
self:F( { CargoNoDestroy=Cargo.NoDestroy } )
if Cargo.NoDestroy then
else
self:Remove( Cargo.Name )
end
end end
end end
end end
--- @type SET_ZONE
-- @extends Core.Set#SET_BASE
--- # SET_ZONE class, extends @{Set#SET_BASE}
--
-- Mission designers can use the @{Set#SET_ZONE} class to build sets of zones of various types.
--
-- ## SET_ZONE constructor
--
-- Create a new SET_ZONE object with the @{#SET_ZONE.New} method:
--
-- * @{#SET_ZONE.New}: Creates a new SET_ZONE object.
--
-- ## Add or Remove ZONEs from SET_ZONE
--
-- ZONEs can be added and removed using the @{Set#SET_ZONE.AddZonesByName} and @{Set#SET_ZONE.RemoveZonesByName} respectively.
-- These methods take a single ZONE name or an array of ZONE names to be added or removed from SET_ZONE.
--
-- ## 5.3) SET_ZONE filter criteria
--
-- You can set filter criteria to build the collection of zones in SET_ZONE.
-- Filter criteria are defined by:
--
-- * @{#SET_ZONE.FilterPrefixes}: Builds the SET_ZONE with the zones having a certain text pattern of prefix.
--
-- Once the filter criteria have been set for the SET_ZONE, you can start filtering using:
--
-- * @{#SET_ZONE.FilterStart}: Starts the filtering of the zones within the SET_ZONE.
--
-- ## 5.4) SET_ZONE iterators
--
-- Once the filters have been defined and the SET_ZONE has been built, you can iterate the SET_ZONE with the available iterator methods.
-- The iterator methods will walk the SET_ZONE set, and call for each airbase within the set a function that you provide.
-- The following iterator methods are currently available within the SET_ZONE:
--
-- * @{#SET_ZONE.ForEachZone}: Calls a function for each zone it finds within the SET_ZONE.
--
-- ===
-- @field #SET_ZONE SET_ZONE
SET_ZONE = {
ClassName = "SET_ZONE",
Zones = {},
Filter = {
Prefixes = nil,
},
FilterMeta = {
},
}
--- Creates a new SET_ZONE object, building a set of zones.
-- @param #SET_ZONE self
-- @return #SET_ZONE self
-- @usage
-- -- Define a new SET_ZONE Object. The DatabaseSet will contain a reference to all Zones.
-- DatabaseSet = SET_ZONE:New()
function SET_ZONE:New()
-- Inherits from BASE
local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.ZONES ) )
return self
end
--- Add ZONEs to SET_ZONE.
-- @param Core.Set#SET_ZONE self
-- @param #string AddZoneNames A single name or an array of ZONE_BASE names.
-- @return self
function SET_ZONE:AddZonesByName( AddZoneNames )
local AddZoneNamesArray = ( type( AddZoneNames ) == "table" ) and AddZoneNames or { AddZoneNames }
for AddAirbaseID, AddZoneName in pairs( AddZoneNamesArray ) do
self:Add( AddZoneName, ZONE:FindByName( AddZoneName ) )
end
return self
end
--- Remove ZONEs from SET_ZONE.
-- @param Core.Set#SET_ZONE self
-- @param Core.Zone#ZONE_BASE RemoveZoneNames A single name or an array of ZONE_BASE names.
-- @return self
function SET_ZONE:RemoveZonesByName( RemoveZoneNames )
local RemoveZoneNamesArray = ( type( RemoveZoneNames ) == "table" ) and RemoveZoneNames or { RemoveZoneNames }
for RemoveZoneID, RemoveZoneName in pairs( RemoveZoneNamesArray ) do
self:Remove( RemoveZoneName )
end
return self
end
--- Finds a Zone based on the Zone Name.
-- @param #SET_ZONE self
-- @param #string ZoneName
-- @return Core.Zone#ZONE_BASE The found Zone.
function SET_ZONE:FindZone( ZoneName )
local ZoneFound = self.Set[ZoneName]
return ZoneFound
end
--- Get a random zone from the set.
-- @param #SET_ZONE self
-- @return Core.Zone#ZONE_BASE The random Zone.
function SET_ZONE:GetRandomZone()
local Index = self.Index
local ZoneFound = nil
while not ZoneFound do
local ZoneRandom = math.random( 1, #Index )
ZoneFound = self.Set[Index[ZoneRandom]]
end
return ZoneFound
end
--- Builds a set of zones of defined zone prefixes.
-- All the zones starting with the given prefixes will be included within the set.
-- @param #SET_ZONE self
-- @param #string Prefixes The prefix of which the zone name starts with.
-- @return #SET_ZONE self
function SET_ZONE:FilterPrefixes( Prefixes )
if not self.Filter.Prefixes then
self.Filter.Prefixes = {}
end
if type( Prefixes ) ~= "table" then
Prefixes = { Prefixes }
end
for PrefixID, Prefix in pairs( Prefixes ) do
self.Filter.Prefixes[Prefix] = Prefix
end
return self
end
--- Starts the filtering.
-- @param #SET_ZONE self
-- @return #SET_ZONE self
function SET_ZONE:FilterStart()
if _DATABASE then
-- We initialize the first set.
for ObjectName, Object in pairs( self.Database ) do
if self:IsIncludeObject( Object ) then
self:Add( ObjectName, Object )
else
self:RemoveZonesByName( ObjectName )
end
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!
-- @param #SET_ZONE self
-- @param Core.Event#EVENTDATA Event
-- @return #string The name of the AIRBASE
-- @return #table The AIRBASE
function SET_ZONE:AddInDatabase( Event )
self:F3( { Event } )
return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName]
end
--- Handles the Database to check on any event that Object exists in the Database.
-- This is required, because sometimes the _DATABASE event gets called later than the SET_BASE event or vise versa!
-- @param #SET_ZONE self
-- @param Core.Event#EVENTDATA Event
-- @return #string The name of the AIRBASE
-- @return #table The AIRBASE
function SET_ZONE:FindInDatabase( Event )
self:F3( { Event } )
return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName]
end
--- Iterate the SET_ZONE and call an interator function for each ZONE, providing the ZONE and optional parameters.
-- @param #SET_ZONE self
-- @param #function IteratorFunction The function that will be called when there is an alive ZONE in the SET_ZONE. The function needs to accept a AIRBASE parameter.
-- @return #SET_ZONE self
function SET_ZONE:ForEachZone( IteratorFunction, ... )
self:F2( arg )
self:ForEach( IteratorFunction, arg, self:GetSet() )
return self
end
---
-- @param #SET_ZONE self
-- @param Core.Zone#ZONE_BASE MZone
-- @return #SET_ZONE self
function SET_ZONE:IsIncludeObject( MZone )
self:F2( MZone )
local MZoneInclude = true
if MZone then
local MZoneName = MZone:GetName()
if self.Filter.Prefixes then
local MZonePrefix = false
for ZonePrefixId, ZonePrefix in pairs( self.Filter.Prefixes ) do
self:T3( { "Prefix:", string.find( MZoneName, ZonePrefix, 1 ), ZonePrefix } )
if string.find( MZoneName, ZonePrefix, 1 ) then
MZonePrefix = true
end
end
self:T( { "Evaluated Prefix", MZonePrefix } )
MZoneInclude = MZoneInclude and MZonePrefix
end
end
self:T2( MZoneInclude )
return MZoneInclude
end

View File

@@ -288,20 +288,20 @@ function SPAWN:New( SpawnTemplatePrefix )
self.SpawnIndex = 0 self.SpawnIndex = 0
self.SpawnCount = 0 -- The internal counter of the amount of spawning the has happened since SpawnStart. self.SpawnCount = 0 -- The internal counter of the amount of spawning the has happened since SpawnStart.
self.AliveUnits = 0 -- Contains the counter how many units are currently alive self.AliveUnits = 0 -- Contains the counter how many units are currently alive
self.SpawnIsScheduled = false -- Reflects if the spawning for this SpawnTemplatePrefix is going to be scheduled or not. self.SpawnIsScheduled = false -- Reflects if the spawning for this SpawnTemplatePrefix is going to be scheduled or not.
self.SpawnTemplate = self._GetTemplate( self, SpawnTemplatePrefix ) -- Contains the template structure for a Group Spawn from the Mission Editor. Note that this group must have lateActivation always on!!! self.SpawnTemplate = self._GetTemplate( self, SpawnTemplatePrefix ) -- Contains the template structure for a Group Spawn from the Mission Editor. Note that this group must have lateActivation always on!!!
self.Repeat = false -- Don't repeat the group from Take-Off till Landing and back Take-Off by ReSpawning. self.Repeat = false -- Don't repeat the group from Take-Off till Landing and back Take-Off by ReSpawning.
self.UnControlled = false -- When working in UnControlled mode, all planes are Spawned in UnControlled mode before the scheduler starts. self.UnControlled = false -- When working in UnControlled mode, all planes are Spawned in UnControlled mode before the scheduler starts.
self.SpawnInitLimit = false -- By default, no InitLimit self.SpawnInitLimit = false -- By default, no InitLimit
self.SpawnMaxUnitsAlive = 0 -- The maximum amount of groups that can be alive of SpawnTemplatePrefix at the same time. self.SpawnMaxUnitsAlive = 0 -- The maximum amount of groups that can be alive of SpawnTemplatePrefix at the same time.
self.SpawnMaxGroups = 0 -- The maximum amount of groups that can be spawned. self.SpawnMaxGroups = 0 -- The maximum amount of groups that can be spawned.
self.SpawnRandomize = false -- Sets the randomization flag of new Spawned units to false. self.SpawnRandomize = false -- Sets the randomization flag of new Spawned units to false.
self.SpawnVisible = false -- Flag that indicates if all the Groups of the SpawnGroup need to be visible when Spawned. self.SpawnVisible = false -- Flag that indicates if all the Groups of the SpawnGroup need to be visible when Spawned.
self.AIOnOff = true -- The AI is on by default when spawning a group. self.AIOnOff = true -- The AI is on by default when spawning a group.
self.SpawnUnControlled = false self.SpawnUnControlled = false
self.SpawnInitKeepUnitNames = false -- Overwrite unit names by default with group name. self.SpawnInitKeepUnitNames = false -- Overwrite unit names by default with group name.
self.DelayOnOff = false -- No intial delay when spawning the first group. self.DelayOnOff = false -- No intial delay when spawning the first group.
self.Grouping = nil -- No grouping self.Grouping = nil -- No grouping
self.SpawnGroups = {} -- Array containing the descriptions of each Group to be Spawned. self.SpawnGroups = {} -- Array containing the descriptions of each Group to be Spawned.
else else
@@ -334,19 +334,19 @@ function SPAWN:NewWithAlias( SpawnTemplatePrefix, SpawnAliasPrefix )
self.SpawnIndex = 0 self.SpawnIndex = 0
self.SpawnCount = 0 -- The internal counter of the amount of spawning the has happened since SpawnStart. self.SpawnCount = 0 -- The internal counter of the amount of spawning the has happened since SpawnStart.
self.AliveUnits = 0 -- Contains the counter how many units are currently alive self.AliveUnits = 0 -- Contains the counter how many units are currently alive
self.SpawnIsScheduled = false -- Reflects if the spawning for this SpawnTemplatePrefix is going to be scheduled or not. self.SpawnIsScheduled = false -- Reflects if the spawning for this SpawnTemplatePrefix is going to be scheduled or not.
self.SpawnTemplate = self._GetTemplate( self, SpawnTemplatePrefix ) -- Contains the template structure for a Group Spawn from the Mission Editor. Note that this group must have lateActivation always on!!! self.SpawnTemplate = self._GetTemplate( self, SpawnTemplatePrefix ) -- Contains the template structure for a Group Spawn from the Mission Editor. Note that this group must have lateActivation always on!!!
self.Repeat = false -- Don't repeat the group from Take-Off till Landing and back Take-Off by ReSpawning. self.Repeat = false -- Don't repeat the group from Take-Off till Landing and back Take-Off by ReSpawning.
self.UnControlled = false -- When working in UnControlled mode, all planes are Spawned in UnControlled mode before the scheduler starts. self.UnControlled = false -- When working in UnControlled mode, all planes are Spawned in UnControlled mode before the scheduler starts.
self.SpawnInitLimit = false -- By default, no InitLimit self.SpawnInitLimit = false -- By default, no InitLimit
self.SpawnMaxUnitsAlive = 0 -- The maximum amount of groups that can be alive of SpawnTemplatePrefix at the same time. self.SpawnMaxUnitsAlive = 0 -- The maximum amount of groups that can be alive of SpawnTemplatePrefix at the same time.
self.SpawnMaxGroups = 0 -- The maximum amount of groups that can be spawned. self.SpawnMaxGroups = 0 -- The maximum amount of groups that can be spawned.
self.SpawnRandomize = false -- Sets the randomization flag of new Spawned units to false. self.SpawnRandomize = false -- Sets the randomization flag of new Spawned units to false.
self.SpawnVisible = false -- Flag that indicates if all the Groups of the SpawnGroup need to be visible when Spawned. self.SpawnVisible = false -- Flag that indicates if all the Groups of the SpawnGroup need to be visible when Spawned.
self.AIOnOff = true -- The AI is on by default when spawning a group. self.AIOnOff = true -- The AI is on by default when spawning a group.
self.SpawnUnControlled = false self.SpawnUnControlled = false
self.SpawnInitKeepUnitNames = false -- Overwrite unit names by default with group name. self.SpawnInitKeepUnitNames = false -- Overwrite unit names by default with group name.
self.DelayOnOff = false -- No intial delay when spawning the first group. self.DelayOnOff = false -- No intial delay when spawning the first group.
self.Grouping = nil self.Grouping = nil
self.SpawnGroups = {} -- Array containing the descriptions of each Group to be Spawned. self.SpawnGroups = {} -- Array containing the descriptions of each Group to be Spawned.
@@ -361,6 +361,55 @@ function SPAWN:NewWithAlias( SpawnTemplatePrefix, SpawnAliasPrefix )
end end
--- Creates a new SPAWN instance to create new groups based on the provided template.
-- @param #SPAWN self
-- @param #table SpawnTemplate is the Template of the Group. This must be a valid Group Template structure!
-- @param #string SpawnTemplatePrefix is the name of the Group that will be given at each spawn.
-- @param #string SpawnAliasPrefix (optional) is the name that will be given to the Group at runtime.
-- @return #SPAWN
-- @usage
-- -- Create a new SPAWN object based on a Group Template defined from scratch.
-- Spawn_BE_KA50 = SPAWN:NewWithAlias( 'BE KA-50@RAMP-Ground Defense', 'Helicopter Attacking a City' )
-- @usage
-- -- Create a new CSAR_Spawn object based on a normal Group Template to spawn a soldier.
-- local CSAR_Spawn = SPAWN:NewWithFromTemplate( Template, "CSAR", "Pilot" )
function SPAWN:NewFromTemplate( SpawnTemplate, SpawnTemplatePrefix, SpawnAliasPrefix )
local self = BASE:Inherit( self, BASE:New() )
self:F( { SpawnTemplate, SpawnTemplatePrefix, SpawnAliasPrefix } )
if SpawnTemplate then
self.SpawnTemplate = SpawnTemplate -- Contains the template structure for a Group Spawn from the Mission Editor. Note that this group must have lateActivation always on!!!
self.SpawnTemplatePrefix = SpawnTemplatePrefix
self.SpawnAliasPrefix = SpawnAliasPrefix
self.SpawnIndex = 0
self.SpawnCount = 0 -- The internal counter of the amount of spawning the has happened since SpawnStart.
self.AliveUnits = 0 -- Contains the counter how many units are currently alive
self.SpawnIsScheduled = false -- Reflects if the spawning for this SpawnTemplatePrefix is going to be scheduled or not.
self.Repeat = false -- Don't repeat the group from Take-Off till Landing and back Take-Off by ReSpawning.
self.UnControlled = false -- When working in UnControlled mode, all planes are Spawned in UnControlled mode before the scheduler starts.
self.SpawnInitLimit = false -- By default, no InitLimit.
self.SpawnMaxUnitsAlive = 0 -- The maximum amount of groups that can be alive of SpawnTemplatePrefix at the same time.
self.SpawnMaxGroups = 0 -- The maximum amount of groups that can be spawned.
self.SpawnRandomize = false -- Sets the randomization flag of new Spawned units to false.
self.SpawnVisible = false -- Flag that indicates if all the Groups of the SpawnGroup need to be visible when Spawned.
self.AIOnOff = true -- The AI is on by default when spawning a group.
self.SpawnUnControlled = false
self.SpawnInitKeepUnitNames = false -- Overwrite unit names by default with group name.
self.DelayOnOff = false -- No intial delay when spawning the first group.
self.Grouping = nil
self.SpawnGroups = {} -- Array containing the descriptions of each Group to be Spawned.
else
error( "There is no template provided for SpawnTemplatePrefix = '" .. SpawnTemplatePrefix .. "'" )
end
self:SetEventPriority( 5 )
self.SpawnHookScheduler = SCHEDULER:New( nil )
return self
end
--- Limits the Maximum amount of Units that can be alive at the same time, and the maximum amount of groups that can be spawned. --- Limits the Maximum amount of Units that can be alive at the same time, and the maximum amount of groups that can be spawned.
-- Note that this method is exceptionally important to balance the performance of the mission. Depending on the machine etc, a mission can only process a maximum amount of units. -- Note that this method is exceptionally important to balance the performance of the mission. Depending on the machine etc, a mission can only process a maximum amount of units.
-- If the time interval must be short, but there should not be more Units or Groups alive than a maximum amount of units, then this method should be used... -- If the time interval must be short, but there should not be more Units or Groups alive than a maximum amount of units, then this method should be used...
@@ -404,6 +453,57 @@ function SPAWN:InitKeepUnitNames()
return self return self
end end
--- Defines the Heading for the new spawned units.
-- The heading can be given as one fixed degree, or can be randomized between minimum and maximum degrees.
-- @param #SPAWN self
-- @param #number HeadingMin The minimum or fixed heading in degrees.
-- @param #number HeadingMax (optional) The maximum heading in degrees. This there is no maximum heading, then the heading will be fixed for all units using minimum heading.
-- @return #SPAWN self
-- @usage
--
-- Spawn = SPAWN:New( ... )
--
-- -- Spawn the units pointing to 100 degrees.
-- Spawn:InitHeading( 100 )
--
-- -- Spawn the units pointing between 100 and 150 degrees.
-- Spawn:InitHeading( 100, 150 )
--
function SPAWN:InitHeading( HeadingMin, HeadingMax )
self:F( )
self.SpawnInitHeadingMin = HeadingMin
self.SpawnInitHeadingMax = HeadingMax
return self
end
function SPAWN:InitCoalition( Coalition )
self:F( )
self.SpawnInitCoalition = Coalition
return self
end
function SPAWN:InitCountry( Country )
self:F( )
self.SpawnInitCountry = Country
return self
end
function SPAWN:InitCategory( Category )
self:F( )
self.SpawnInitCategory = Category
return self
end
--- Randomizes the defined route of the SpawnTemplatePrefix group in the ME. This is very useful to define extra variation of the behaviour of groups. --- Randomizes the defined route of the SpawnTemplatePrefix group in the ME. This is very useful to define extra variation of the behaviour of groups.
-- @param #SPAWN self -- @param #SPAWN self
@@ -941,6 +1041,18 @@ function SPAWN:SpawnWithIndex( SpawnIndex )
end end
end end
-- If Heading is given, point all the units towards the given Heading.
if self.SpawnInitHeadingMin then
for UnitID = 1, #SpawnTemplate.units do
SpawnTemplate.units[UnitID].heading = self.SpawnInitHeadingMax and math.random( self.SpawnInitHeadingMin, self.SpawnInitHeadingMax ) or self.SpawnInitHeadingMin
end
end
SpawnTemplate.CategoryID = self.SpawnInitCategory or SpawnTemplate.CategoryID
SpawnTemplate.CountryID = self.SpawnInitCountry or SpawnTemplate.CountryID
SpawnTemplate.CoalitionID = self.SpawnInitCoalition or SpawnTemplate.CoalitionID
if SpawnTemplate.CategoryID == Group.Category.HELICOPTER or SpawnTemplate.CategoryID == Group.Category.AIRPLANE then if SpawnTemplate.CategoryID == Group.Category.HELICOPTER or SpawnTemplate.CategoryID == Group.Category.AIRPLANE then
if SpawnTemplate.route.points[1].type == "TakeOffParking" then if SpawnTemplate.route.points[1].type == "TakeOffParking" then
SpawnTemplate.uncontrolled = self.SpawnUnControlled SpawnTemplate.uncontrolled = self.SpawnUnControlled
@@ -1247,14 +1359,20 @@ function SPAWN:SpawnFromVec3( Vec3, SpawnIndex )
self:T( { "Current point of ", self.SpawnTemplatePrefix, Vec3 } ) self:T( { "Current point of ", self.SpawnTemplatePrefix, Vec3 } )
local TemplateHeight = SpawnTemplate.route.points[1].alt local TemplateHeight = SpawnTemplate.route and SpawnTemplate.route.points[1].alt or nil
SpawnTemplate.route = SpawnTemplate.route or {}
SpawnTemplate.route.points = SpawnTemplate.route.points or {}
SpawnTemplate.route.points[1] = SpawnTemplate.route.points[1] or {}
SpawnTemplate.route.points[1].x = SpawnTemplate.route.points[1].x or 0
SpawnTemplate.route.points[1].y = SpawnTemplate.route.points[1].y or 0
-- Translate the position of the Group Template to the Vec3. -- Translate the position of the Group Template to the Vec3.
for UnitID = 1, #SpawnTemplate.units do for UnitID = 1, #SpawnTemplate.units do
self:T( 'Before Translation SpawnTemplate.units['..UnitID..'].x = ' .. SpawnTemplate.units[UnitID].x .. ', SpawnTemplate.units['..UnitID..'].y = ' .. SpawnTemplate.units[UnitID].y ) --self:T( 'Before Translation SpawnTemplate.units['..UnitID..'].x = ' .. SpawnTemplate.units[UnitID].x .. ', SpawnTemplate.units['..UnitID..'].y = ' .. SpawnTemplate.units[UnitID].y )
local UnitTemplate = SpawnTemplate.units[UnitID] local UnitTemplate = SpawnTemplate.units[UnitID]
local SX = UnitTemplate.x local SX = UnitTemplate.x or 0
local SY = UnitTemplate.y local SY = UnitTemplate.y or 0
local BX = SpawnTemplate.route.points[1].x local BX = SpawnTemplate.route.points[1].x
local BY = SpawnTemplate.route.points[1].y local BY = SpawnTemplate.route.points[1].y
local TX = Vec3.x + ( SX - BX ) local TX = Vec3.x + ( SX - BX )
@@ -1266,7 +1384,6 @@ function SPAWN:SpawnFromVec3( Vec3, SpawnIndex )
end end
self:T( 'After Translation SpawnTemplate.units['..UnitID..'].x = ' .. SpawnTemplate.units[UnitID].x .. ', SpawnTemplate.units['..UnitID..'].y = ' .. SpawnTemplate.units[UnitID].y ) self:T( 'After Translation SpawnTemplate.units['..UnitID..'].x = ' .. SpawnTemplate.units[UnitID].x .. ', SpawnTemplate.units['..UnitID..'].y = ' .. SpawnTemplate.units[UnitID].y )
end end
SpawnTemplate.route.points[1].x = Vec3.x SpawnTemplate.route.points[1].x = Vec3.x
SpawnTemplate.route.points[1].y = Vec3.z SpawnTemplate.route.points[1].y = Vec3.z
if SpawnTemplate.CategoryID ~= Group.Category.SHIP then if SpawnTemplate.CategoryID ~= Group.Category.SHIP then
@@ -1283,6 +1400,47 @@ function SPAWN:SpawnFromVec3( Vec3, SpawnIndex )
return nil return nil
end end
--- Will spawn a group from a Coordinate in 3D space.
-- This method is mostly advisable to be used if you want to simulate spawning units in the air, like helicopters or airplanes.
-- 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#Coordinate Coordinate 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.
-- @return #nil Nothing was spawned.
function SPAWN:SpawnFromCoordinate( Coordinate, SpawnIndex )
self:F( { self.SpawnTemplatePrefix, SpawnIndex } )
return self:SpawnFromVec3( Coordinate:GetVec3(), SpawnIndex )
end
--- Will spawn a group from a PointVec3 in 3D space.
-- This method is mostly advisable to be used if you want to simulate spawning units in the air, like helicopters or airplanes.
-- 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 #number SpawnIndex (optional) The index which group to spawn within the given zone.
-- @return Wrapper.Group#GROUP that was spawned.
-- @return #nil Nothing was spawned.
-- @usage
--
-- local SpawnPointVec3 = ZONE:New( ZoneName ):GetPointVec3( 2000 ) -- Get the center of the ZONE object at 2000 meters from the ground.
--
-- -- Spawn at the zone center position at 2000 meters from the ground!
-- SpawnAirplanes:SpawnFromPointVec3( SpawnPointVec3 )
--
function SPAWN:SpawnFromPointVec3( PointVec3, SpawnIndex )
self:F( { self.SpawnTemplatePrefix, SpawnIndex } )
return self:SpawnFromVec3( PointVec3:GetVec3(), SpawnIndex )
end
--- Will spawn a group from a Vec2 in 3D space. --- Will spawn a group from a Vec2 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. -- 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. -- Note that each point in the route assigned to the spawning group is reset to the point of the spawn.
@@ -1317,6 +1475,35 @@ function SPAWN:SpawnFromVec2( Vec2, MinHeight, MaxHeight, SpawnIndex )
end end
--- Will spawn a group from a POINT_VEC2 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 #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.
-- @return Wrapper.Group#GROUP that was spawned.
-- @return #nil Nothing was spawned.
-- @usage
--
-- local SpawnPointVec2 = ZONE:New( ZoneName ):GetPointVec2()
--
-- -- Spawn at the zone center position at the height specified in the ME of the group template!
-- SpawnAirplanes:SpawnFromPointVec2( SpawnPointVec2 )
--
-- -- Spawn from the static position at the height randomized between 2000 and 4000 meters.
-- SpawnAirplanes:SpawnFromPointVec2( SpawnPointVec2, 2000, 4000 )
--
function SPAWN:SpawnFromPointVec2( PointVec2, MinHeight, MaxHeight, SpawnIndex )
self:F( { self.SpawnTemplatePrefix, self.SpawnIndex } )
return self:SpawnFromVec2( PointVec2:GetVec2(), MinHeight, MaxHeight, SpawnIndex )
end
--- Will spawn a group from a hosting unit. This method is mostly advisable to be used if you want to simulate spawning from air units, like helicopters, which are dropping infantry into a defined Landing Zone. --- Will spawn a group from a hosting unit. This method is mostly advisable to be used if you want to simulate spawning from air units, like helicopters, which are dropping infantry into a defined Landing Zone.
-- Note that each point in the route assigned to the spawning group is reset to the point of the spawn. -- 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. -- You can use the returned group to further define the route to be followed.
@@ -1693,7 +1880,10 @@ function SPAWN:_GetTemplate( SpawnTemplatePrefix )
local SpawnTemplate = nil local SpawnTemplate = nil
SpawnTemplate = routines.utils.deepCopy( _DATABASE.Templates.Groups[SpawnTemplatePrefix].Template ) local Template = _DATABASE.Templates.Groups[SpawnTemplatePrefix].Template
self:F( { Template = Template } )
SpawnTemplate = UTILS.DeepCopy( _DATABASE.Templates.Groups[SpawnTemplatePrefix].Template )
if SpawnTemplate == nil then if SpawnTemplate == nil then
error( 'No Template returned for SpawnTemplatePrefix = ' .. SpawnTemplatePrefix ) error( 'No Template returned for SpawnTemplatePrefix = ' .. SpawnTemplatePrefix )
@@ -1715,7 +1905,12 @@ end
function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) --R2.2 function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) --R2.2
self:F( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix } ) self:F( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix } )
local SpawnTemplate = self:_GetTemplate( SpawnTemplatePrefix ) -- if not self.SpawnTemplate then
-- self.SpawnTemplate = self:_GetTemplate( SpawnTemplatePrefix )
-- end
local SpawnTemplate = self:_GetTemplate( SpawnTemplatePrefix )
--local SpawnTemplate = self.SpawnTemplate
SpawnTemplate.name = self:SpawnGroupName( SpawnIndex ) SpawnTemplate.name = self:SpawnGroupName( SpawnIndex )
SpawnTemplate.groupId = nil SpawnTemplate.groupId = nil
@@ -1808,7 +2003,7 @@ function SPAWN:_RandomizeTemplate( SpawnIndex )
if self.SpawnRandomizeTemplate then if self.SpawnRandomizeTemplate then
self.SpawnGroups[SpawnIndex].SpawnTemplatePrefix = self.SpawnTemplatePrefixTable[ math.random( 1, #self.SpawnTemplatePrefixTable ) ] self.SpawnGroups[SpawnIndex].SpawnTemplatePrefix = self.SpawnTemplatePrefixTable[ math.random( 1, #self.SpawnTemplatePrefixTable ) ]
self.SpawnGroups[SpawnIndex].SpawnTemplate = self:_Prepare( self.SpawnGroups[SpawnIndex].SpawnTemplatePrefix, SpawnIndex ) self.SpawnGroups[SpawnIndex].SpawnTemplate = self:_Prepare( self.SpawnGroups[SpawnIndex].SpawnTemplatePrefix, SpawnIndex )
self.SpawnGroups[SpawnIndex].SpawnTemplate.route = routines.utils.deepCopy( self.SpawnTemplate.route ) self.SpawnGroups[SpawnIndex].SpawnTemplate.route = UTILS.DeepCopy( self.SpawnTemplate.route )
self.SpawnGroups[SpawnIndex].SpawnTemplate.x = self.SpawnTemplate.x self.SpawnGroups[SpawnIndex].SpawnTemplate.x = self.SpawnTemplate.x
self.SpawnGroups[SpawnIndex].SpawnTemplate.y = self.SpawnTemplate.y self.SpawnGroups[SpawnIndex].SpawnTemplate.y = self.SpawnTemplate.y
self.SpawnGroups[SpawnIndex].SpawnTemplate.start_time = self.SpawnTemplate.start_time self.SpawnGroups[SpawnIndex].SpawnTemplate.start_time = self.SpawnTemplate.start_time

View File

@@ -81,14 +81,16 @@ SPAWNSTATIC = {
-- @param #SPAWNSTATIC self -- @param #SPAWNSTATIC self
-- @param #string SpawnTemplatePrefix is the name of the Group in the ME that defines the Template. Each new group will have the name starting with SpawnTemplatePrefix. -- @param #string SpawnTemplatePrefix is the name of the Group in the ME that defines the Template. Each new group will have the name starting with SpawnTemplatePrefix.
-- @return #SPAWNSTATIC -- @return #SPAWNSTATIC
function SPAWNSTATIC:NewFromStatic( SpawnTemplatePrefix, CountryID ) --R2.1 function SPAWNSTATIC:NewFromStatic( SpawnTemplatePrefix, SpawnCountryID ) --R2.1
local self = BASE:Inherit( self, BASE:New() ) -- #SPAWNSTATIC local self = BASE:Inherit( self, BASE:New() ) -- #SPAWNSTATIC
self:F( { SpawnTemplatePrefix } ) self:F( { SpawnTemplatePrefix } )
local TemplateStatic = StaticObject.getByName( SpawnTemplatePrefix ) local TemplateStatic, CoalitionID, CategoryID, CountryID = _DATABASE:GetStaticUnitTemplate( SpawnTemplatePrefix )
if TemplateStatic then if TemplateStatic then
self.SpawnTemplatePrefix = SpawnTemplatePrefix self.SpawnTemplatePrefix = SpawnTemplatePrefix
self.CountryID = CountryID self.CountryID = SpawnCountryID or CountryID
self.CategoryID = CategoryID
self.CoalitionID = CoalitionID
self.SpawnIndex = 0 self.SpawnIndex = 0
else else
error( "SPAWNSTATIC:New: There is no group declared in the mission editor with SpawnTemplatePrefix = '" .. SpawnTemplatePrefix .. "'" ) error( "SPAWNSTATIC:New: There is no group declared in the mission editor with SpawnTemplatePrefix = '" .. SpawnTemplatePrefix .. "'" )
@@ -116,6 +118,7 @@ function SPAWNSTATIC:NewFromType( SpawnTypeName, SpawnShapeName, SpawnCategory,
return self return self
end end
--- Creates a new @{Static} at the original position. --- Creates a new @{Static} at the original position.
-- @param #SPAWNSTATIC self -- @param #SPAWNSTATIC self
-- @param #number Heading The heading of the static, which is a number in degrees from 0 to 360. -- @param #number Heading The heading of the static, which is a number in degrees from 0 to 360.
@@ -124,22 +127,28 @@ end
function SPAWNSTATIC:Spawn( Heading, NewName ) --R2.3 function SPAWNSTATIC:Spawn( Heading, NewName ) --R2.3
self:F( { Heading, NewName } ) self:F( { Heading, NewName } )
local CountryName = _DATABASE.COUNTRY_NAME[self.CountryID]
local StaticTemplate = _DATABASE:GetStaticUnitTemplate( self.SpawnTemplatePrefix ) local StaticTemplate = _DATABASE:GetStaticUnitTemplate( self.SpawnTemplatePrefix )
StaticTemplate.name = NewName or string.format("%s#%05d", self.SpawnTemplatePrefix, self.SpawnIndex ) if StaticTemplate then
StaticTemplate.heading = ( Heading / 180 ) * math.pi
StaticTemplate.CountryID = nil local CountryID = self.CountryID
StaticTemplate.CoalitionID = nil local CountryName = _DATABASE.COUNTRY_NAME[CountryID]
StaticTemplate.CategoryID = nil
local Static = coalition.addStaticObject( self.CountryID, StaticTemplate ) StaticTemplate.name = NewName or string.format("%s#%05d", self.SpawnTemplatePrefix, self.SpawnIndex )
StaticTemplate.heading = ( Heading / 180 ) * math.pi
StaticTemplate.CountryID = nil
StaticTemplate.CoalitionID = nil
StaticTemplate.CategoryID = nil
local Static = coalition.addStaticObject( CountryID, StaticTemplate )
self.SpawnIndex = self.SpawnIndex + 1
self.SpawnIndex = self.SpawnIndex + 1 return Static
end
return Static
return nil
end end
@@ -153,32 +162,101 @@ end
function SPAWNSTATIC:SpawnFromPointVec2( PointVec2, Heading, NewName ) --R2.1 function SPAWNSTATIC:SpawnFromPointVec2( PointVec2, Heading, NewName ) --R2.1
self:F( { PointVec2, Heading, NewName } ) self:F( { PointVec2, Heading, NewName } )
local CountryName = _DATABASE.COUNTRY_NAME[self.CountryID] local StaticTemplate = _DATABASE:GetStaticUnitTemplate( self.SpawnTemplatePrefix )
if StaticTemplate then
local CountryID = self.CountryID
local CountryName = _DATABASE.COUNTRY_NAME[CountryID]
StaticTemplate.x = PointVec2.x
StaticTemplate.y = PointVec2.z
StaticTemplate.units = nil
StaticTemplate.route = nil
StaticTemplate.groupId = nil
StaticTemplate.name = NewName or string.format("%s#%05d", self.SpawnTemplatePrefix, self.SpawnIndex )
StaticTemplate.heading = ( Heading / 180 ) * math.pi
StaticTemplate.CountryID = nil
StaticTemplate.CoalitionID = nil
StaticTemplate.CategoryID = nil
local Static = coalition.addStaticObject( CountryID, StaticTemplate )
self.SpawnIndex = self.SpawnIndex + 1
return Static
end
return nil
end
--- Creates the original @{Static} at a POINT_VEC2.
-- @param #SPAWNSTATIC self
-- @param Core.Point#POINT_VEC2 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 (optional) The name of the new static.
-- @return #SPAWNSTATIC
function SPAWNSTATIC:ReSpawn()
local StaticTemplate = _DATABASE:GetStaticUnitTemplate( self.SpawnTemplatePrefix ) local StaticTemplate = _DATABASE:GetStaticUnitTemplate( self.SpawnTemplatePrefix )
StaticTemplate.x = PointVec2.x if StaticTemplate then
StaticTemplate.y = PointVec2.z
StaticTemplate.units = nil local CountryID = self.CountryID
StaticTemplate.route = nil local CountryName = _DATABASE.COUNTRY_NAME[CountryID]
StaticTemplate.groupId = nil
StaticTemplate.units = nil
StaticTemplate.route = nil
StaticTemplate.groupId = nil
StaticTemplate.CountryID = nil
StaticTemplate.CoalitionID = nil
StaticTemplate.CategoryID = nil
local Static = coalition.addStaticObject( CountryID, StaticTemplate )
return Static
end
StaticTemplate.name = NewName or string.format("%s#%05d", self.SpawnTemplatePrefix, self.SpawnIndex ) return nil
StaticTemplate.heading = ( Heading / 180 ) * math.pi
StaticTemplate.CountryID = nil
StaticTemplate.CoalitionID = nil
StaticTemplate.CategoryID = nil
local Static = coalition.addStaticObject( self.CountryID, StaticTemplate )
self.SpawnIndex = self.SpawnIndex + 1
return Static
end end
--- Creates the original @{Static} at a POINT_VEC2.
-- @param #SPAWNSTATIC self
-- @param Core.Point#COORDINATE Coordinate 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.
-- @return #SPAWNSTATIC
function SPAWNSTATIC:ReSpawnAt( Coordinate, Heading )
local StaticTemplate = _DATABASE:GetStaticUnitTemplate( self.SpawnTemplatePrefix )
if StaticTemplate then
local CountryID = self.CountryID
StaticTemplate.x = Coordinate.x
StaticTemplate.y = Coordinate.z
StaticTemplate.heading = Heading and ( ( Heading / 180 ) * math.pi ) or StaticTemplate.heading
StaticTemplate.CountryID = nil
StaticTemplate.CoalitionID = nil
StaticTemplate.CategoryID = nil
local Static = coalition.addStaticObject( CountryID, StaticTemplate )
return Static
end
return nil
end
--- Creates a new @{Static} from a @{Zone}. --- Creates a new @{Static} from a @{Zone}.
-- @param #SPAWNSTATIC self -- @param #SPAWNSTATIC self
-- @param Core.Zone#ZONE_BASE Zone The Zone where to spawn the static. -- @param Core.Zone#ZONE_BASE Zone The Zone where to spawn the static.

View File

@@ -114,6 +114,8 @@ function ZONE_BASE:New( ZoneName )
return self return self
end end
--- Returns the name of the zone. --- Returns the name of the zone.
-- @param #ZONE_BASE self -- @param #ZONE_BASE self
-- @return #string The name of the zone. -- @return #string The name of the zone.
@@ -149,10 +151,16 @@ end
-- @param Dcs.DCSTypes#Vec3 Vec3 The point to test. -- @param Dcs.DCSTypes#Vec3 Vec3 The point to test.
-- @return #boolean true if the Vec3 is within the zone. -- @return #boolean true if the Vec3 is within the zone.
function ZONE_BASE:IsVec3InZone( Vec3 ) function ZONE_BASE:IsVec3InZone( Vec3 )
self:F2( Vec3 )
local InZone = self:IsVec2InZone( { x = Vec3.x, y = Vec3.z } ) local InZone = self:IsVec2InZone( { x = Vec3.x, y = Vec3.z } )
return InZone
end
--- Returns if a Coordinate is within the zone.
-- @param #ZONE_BASE self
-- @param Core.Point#COORDINATE Coordinate The coordinate to test.
-- @return #boolean true if the coordinate is within the zone.
function ZONE_BASE:IsCoordinateInZone( Coordinate )
local InZone = self:IsVec2InZone( Coordinate:GetVec2() )
return InZone return InZone
end end
@@ -161,10 +169,7 @@ end
-- @param Core.Point#POINT_VEC2 PointVec2 The PointVec2 to test. -- @param Core.Point#POINT_VEC2 PointVec2 The PointVec2 to test.
-- @return #boolean true if the PointVec2 is within the zone. -- @return #boolean true if the PointVec2 is within the zone.
function ZONE_BASE:IsPointVec2InZone( PointVec2 ) function ZONE_BASE:IsPointVec2InZone( PointVec2 )
self:F2( PointVec2 )
local InZone = self:IsVec2InZone( PointVec2:GetVec2() ) local InZone = self:IsVec2InZone( PointVec2:GetVec2() )
return InZone return InZone
end end
@@ -173,10 +178,7 @@ end
-- @param Core.Point#POINT_VEC3 PointVec3 The PointVec3 to test. -- @param Core.Point#POINT_VEC3 PointVec3 The PointVec3 to test.
-- @return #boolean true if the PointVec3 is within the zone. -- @return #boolean true if the PointVec3 is within the zone.
function ZONE_BASE:IsPointVec3InZone( PointVec3 ) function ZONE_BASE:IsPointVec3InZone( PointVec3 )
self:F2( PointVec3 )
local InZone = self:IsPointVec2InZone( PointVec3 ) local InZone = self:IsPointVec2InZone( PointVec3 )
return InZone return InZone
end end
@@ -185,8 +187,6 @@ end
-- @param #ZONE_BASE self -- @param #ZONE_BASE self
-- @return #nil. -- @return #nil.
function ZONE_BASE:GetVec2() function ZONE_BASE:GetVec2()
self:F2( self.ZoneName )
return nil return nil
end end
@@ -603,6 +603,7 @@ function ZONE_RADIUS:Scan( ObjectCategories )
self.ScanData = {} self.ScanData = {}
self.ScanData.Coalitions = {} self.ScanData.Coalitions = {}
self.ScanData.Scenery = {} self.ScanData.Scenery = {}
self.ScanData.Units = {}
local ZoneCoord = self:GetCoordinate() local ZoneCoord = self:GetCoordinate()
local ZoneRadius = self:GetRadius() local ZoneRadius = self:GetRadius()
@@ -625,6 +626,7 @@ function ZONE_RADIUS:Scan( ObjectCategories )
(ObjectCategory == Object.Category.STATIC and ZoneObject:isExist()) then (ObjectCategory == Object.Category.STATIC and ZoneObject:isExist()) then
local CoalitionDCSUnit = ZoneObject:getCoalition() local CoalitionDCSUnit = ZoneObject:getCoalition()
self.ScanData.Coalitions[CoalitionDCSUnit] = true self.ScanData.Coalitions[CoalitionDCSUnit] = true
self.ScanData.Units[ZoneObject] = ZoneObject
self:F( { Name = ZoneObject:getName(), Coalition = CoalitionDCSUnit } ) self:F( { Name = ZoneObject:getName(), Coalition = CoalitionDCSUnit } )
end end
if ObjectCategory == Object.Category.SCENERY then if ObjectCategory == Object.Category.SCENERY then
@@ -643,6 +645,12 @@ function ZONE_RADIUS:Scan( ObjectCategories )
end end
function ZONE_RADIUS:GetScannedUnits()
return self.ScanData.Units
end
function ZONE_RADIUS:CountScannedCoalitions() function ZONE_RADIUS:CountScannedCoalitions()
local Count = 0 local Count = 0
@@ -954,6 +962,17 @@ function ZONE:New( ZoneName )
return self return self
end end
--- Find a zone in the _DATABASE using the name of the zone.
-- @param #ZONE_BASE self
-- @param #string ZoneName The name of the zone.
-- @return #ZONE_BASE self
function ZONE:FindByName( ZoneName )
local ZoneFound = _DATABASE:FindZone( ZoneName )
return ZoneFound
end
--- @type ZONE_UNIT --- @type ZONE_UNIT
-- @field Wrapper.Unit#UNIT ZoneUNIT -- @field Wrapper.Unit#UNIT ZoneUNIT

View File

@@ -4,7 +4,7 @@
--- @type CoalitionObject --- @type CoalitionObject
-- @extends Dcs.DCSWrapper.Object#Object -- @extends Dcs.DCSWrapper.Object#Object
coalition = {} --#coalition
--- Returns coalition of the object. --- Returns coalition of the object.
-- @function [parent=#CoalitionObject] getCoalition -- @function [parent=#CoalitionObject] getCoalition

View File

@@ -12,3 +12,5 @@
--- @function [parent=#coalition] getCountryCoalition --- @function [parent=#coalition] getCountryCoalition
-- @param #number countryId -- @param #number countryId
-- @return #number coalitionId -- @return #number coalitionId
coalition = coalition -- #coalition

File diff suppressed because it is too large Load Diff

View File

@@ -510,7 +510,6 @@ do -- DETECTION_BASE
-- @param #string Event The Event string. -- @param #string Event The Event string.
-- @param #string To The To State string. -- @param #string To The To State string.
function DETECTION_BASE:onafterDetect(From,Event,To) function DETECTION_BASE:onafterDetect(From,Event,To)
self:F( { From, Event, To } )
local DetectDelay = 0.1 local DetectDelay = 0.1
self.DetectionCount = 0 self.DetectionCount = 0
@@ -533,7 +532,6 @@ do -- DETECTION_BASE
-- @param #string To The To State string. -- @param #string To The To State string.
-- @param Wrapper.Group#GROUP DetectionGroup The Group detecting. -- @param Wrapper.Group#GROUP DetectionGroup The Group detecting.
function DETECTION_BASE:onafterDetectionGroup( From, Event, To, DetectionGroup, DetectionTimeStamp ) function DETECTION_BASE:onafterDetectionGroup( From, Event, To, DetectionGroup, DetectionTimeStamp )
self:F( { From, Event, To } )
self.DetectionRun = self.DetectionRun + 1 self.DetectionRun = self.DetectionRun + 1
@@ -541,7 +539,7 @@ do -- DETECTION_BASE
if DetectionGroup:IsAlive() then if DetectionGroup:IsAlive() then
self:T( { "DetectionGroup is Alive", DetectionGroup:GetName() } ) --self:T( { "DetectionGroup is Alive", DetectionGroup:GetName() } )
local DetectionGroupName = DetectionGroup:GetName() local DetectionGroupName = DetectionGroup:GetName()
local DetectionUnit = DetectionGroup:GetUnit(1) local DetectionUnit = DetectionGroup:GetUnit(1)
@@ -557,7 +555,7 @@ do -- DETECTION_BASE
self.DetectDLINK self.DetectDLINK
) )
self:F( DetectedTargets ) --self:F( DetectedTargets )
for DetectionObjectID, Detection in pairs( DetectedTargets ) do for DetectionObjectID, Detection in pairs( DetectedTargets ) do
local DetectedObject = Detection.object -- Dcs.DCSWrapper.Object#Object local DetectedObject = Detection.object -- Dcs.DCSWrapper.Object#Object
@@ -574,7 +572,7 @@ do -- DETECTION_BASE
self.DetectDLINK self.DetectDLINK
) )
self:T2( { TargetIsDetected = TargetIsDetected, TargetIsVisible = TargetIsVisible, TargetLastTime = TargetLastTime, TargetKnowType = TargetKnowType, TargetKnowDistance = TargetKnowDistance, TargetLastPos = TargetLastPos, TargetLastVelocity = TargetLastVelocity } ) --self:T2( { TargetIsDetected = TargetIsDetected, TargetIsVisible = TargetIsVisible, TargetLastTime = TargetLastTime, TargetKnowType = TargetKnowType, TargetKnowDistance = TargetKnowDistance, TargetLastPos = TargetLastPos, TargetLastVelocity = TargetLastVelocity } )
-- Only process if the target is visible. Detection also returns invisible units. -- Only process if the target is visible. Detection also returns invisible units.
--if Detection.visible == true then --if Detection.visible == true then
@@ -596,7 +594,7 @@ do -- DETECTION_BASE
local DetectedUnitCategory = DetectedObject:getDesc().category local DetectedUnitCategory = DetectedObject:getDesc().category
self:F( { "Detected Target:", DetectionGroupName, DetectedObjectName, DetectedObjectType, Distance, DetectedUnitCategory } ) --self:F( { "Detected Target:", DetectionGroupName, DetectedObjectName, DetectedObjectType, Distance, DetectedUnitCategory } )
-- Calculate Acceptance -- Calculate Acceptance
@@ -644,7 +642,7 @@ do -- DETECTION_BASE
local DistanceProbability = 1 - DistanceProbabilityReversed local DistanceProbability = 1 - DistanceProbabilityReversed
DistanceProbability = DistanceProbability * 30 / 300 DistanceProbability = DistanceProbability * 30 / 300
local Probability = math.random() -- Selects a number between 0 and 1 local Probability = math.random() -- Selects a number between 0 and 1
self:T( { Probability, DistanceProbability } ) --self:T( { Probability, DistanceProbability } )
if Probability > DistanceProbability then if Probability > DistanceProbability then
DetectionAccepted = false DetectionAccepted = false
end end
@@ -660,7 +658,7 @@ do -- DETECTION_BASE
AlphaAngleProbability = AlphaAngleProbability * 30 / 300 AlphaAngleProbability = AlphaAngleProbability * 30 / 300
local Probability = math.random() -- Selects a number between 0 and 1 local Probability = math.random() -- Selects a number between 0 and 1
self:T( { Probability, AlphaAngleProbability } ) --self:T( { Probability, AlphaAngleProbability } )
if Probability > AlphaAngleProbability then if Probability > AlphaAngleProbability then
DetectionAccepted = false DetectionAccepted = false
end end
@@ -677,7 +675,7 @@ do -- DETECTION_BASE
if ZoneObject:IsPointVec2InZone( DetectedObjectVec2 ) == true then if ZoneObject:IsPointVec2InZone( DetectedObjectVec2 ) == true then
local Probability = math.random() -- Selects a number between 0 and 1 local Probability = math.random() -- Selects a number between 0 and 1
self:T( { Probability, ZoneProbability } ) --self:T( { Probability, ZoneProbability } )
if Probability > ZoneProbability then if Probability > ZoneProbability then
DetectionAccepted = false DetectionAccepted = false
break break
@@ -702,7 +700,7 @@ do -- DETECTION_BASE
self.DetectedObjects[DetectedObjectName].Distance = Distance self.DetectedObjects[DetectedObjectName].Distance = Distance
self.DetectedObjects[DetectedObjectName].DetectionTimeStamp = DetectionTimeStamp self.DetectedObjects[DetectedObjectName].DetectionTimeStamp = DetectionTimeStamp
self:F( { DetectedObject = self.DetectedObjects[DetectedObjectName] } ) --self:F( { DetectedObject = self.DetectedObjects[DetectedObjectName] } )
local DetectedUnit = UNIT:FindByName( DetectedObjectName ) local DetectedUnit = UNIT:FindByName( DetectedObjectName )
@@ -716,7 +714,7 @@ do -- DETECTION_BASE
--end --end
end end
self:T2( self.DetectedObjects ) --self:T2( self.DetectedObjects )
end end
if HasDetectedObjects then if HasDetectedObjects then
@@ -726,7 +724,6 @@ do -- DETECTION_BASE
end end
if self.DetectionCount > 0 and self.DetectionRun == self.DetectionCount then if self.DetectionCount > 0 and self.DetectionRun == self.DetectionCount then
self:T( "--> Create Detection Sets" )
-- First check if all DetectedObjects were detected. -- First check if all DetectedObjects were detected.
-- This is important. When there are DetectedObjects in the list, but were not detected, -- This is important. When there are DetectedObjects in the list, but were not detected,
@@ -766,7 +763,6 @@ do -- DETECTION_BASE
local DetectedSet = DetectedItem.Set local DetectedSet = DetectedItem.Set
if DetectedSet:Count() == 0 then if DetectedSet:Count() == 0 then
self:F3( { DetectedItemID = DetectedItemID } )
self:RemoveDetectedItem( DetectedItemID ) self:RemoveDetectedItem( DetectedItemID )
end end
@@ -778,8 +774,7 @@ do -- DETECTION_BASE
-- @param #string UnitName The UnitName that needs to be forgotten from the DetectionItem Sets. -- @param #string UnitName The UnitName that needs to be forgotten from the DetectionItem Sets.
-- @return #DETECTION_BASE -- @return #DETECTION_BASE
function DETECTION_BASE:ForgetDetectedUnit( UnitName ) function DETECTION_BASE:ForgetDetectedUnit( UnitName )
self:F2()
local DetectedItems = self:GetDetectedItems() local DetectedItems = self:GetDetectedItems()
for DetectedItemIndex, DetectedItem in pairs( DetectedItems ) do for DetectedItemIndex, DetectedItem in pairs( DetectedItems ) do
@@ -796,7 +791,6 @@ do -- DETECTION_BASE
-- @param #DETECTION_BASE self -- @param #DETECTION_BASE self
-- @return #DETECTION_BASE -- @return #DETECTION_BASE
function DETECTION_BASE:CreateDetectionItems() function DETECTION_BASE:CreateDetectionItems()
self:F2()
self:F( "Error, in DETECTION_BASE class..." ) self:F( "Error, in DETECTION_BASE class..." )
return self return self
@@ -1179,8 +1173,7 @@ do -- DETECTION_BASE
-- @param Dcs.DCSUnit#Unit.Category Category The category of the unit. -- @param Dcs.DCSUnit#Unit.Category Category The category of the unit.
-- @return #boolean true if there are friendlies nearby -- @return #boolean true if there are friendlies nearby
function DETECTION_BASE:IsFriendliesNearBy( DetectedItem, Category ) function DETECTION_BASE:IsFriendliesNearBy( DetectedItem, Category )
--self:F( { "FriendliesNearBy Test", DetectedItem.FriendliesNearBy } )
self:F( { "FriendliesNearBy Test", DetectedItem.FriendliesNearBy } )
return ( DetectedItem.FriendliesNearBy and DetectedItem.FriendliesNearBy[Category] ~= nil ) or false return ( DetectedItem.FriendliesNearBy and DetectedItem.FriendliesNearBy[Category] ~= nil ) or false
end end
@@ -1237,7 +1230,7 @@ do -- DETECTION_BASE
--- Background worker function to determine if there are friendlies nearby ... --- Background worker function to determine if there are friendlies nearby ...
-- @param #DETECTION_BASE self -- @param #DETECTION_BASE self
function DETECTION_BASE:ReportFriendliesNearBy( TargetData ) function DETECTION_BASE:ReportFriendliesNearBy( TargetData )
self:F( { "Search Friendlies", DetectedItem = TargetData.DetectedItem } ) --self:F( { "Search Friendlies", DetectedItem = TargetData.DetectedItem } )
local DetectedItem = TargetData.DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem local DetectedItem = TargetData.DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem
local DetectedSet = TargetData.DetectedItem.Set local DetectedSet = TargetData.DetectedItem.Set
@@ -1286,7 +1279,7 @@ do -- DETECTION_BASE
if FoundUnitInReportSetGroup == true then if FoundUnitInReportSetGroup == true then
-- If the recce was part of the friendlies found, then check if the recce is part of the allowed friendly unit prefixes. -- If the recce was part of the friendlies found, then check if the recce is part of the allowed friendly unit prefixes.
for PrefixID, Prefix in pairs( self.FriendlyPrefixes or {} ) do for PrefixID, Prefix in pairs( self.FriendlyPrefixes or {} ) do
self:F( { "Friendly Prefix:", Prefix = Prefix } ) --self:F( { "Friendly Prefix:", Prefix = Prefix } )
-- In case a match is found (so a recce unit name is part of the friendly prefixes), then report that recce to be part of the friendlies. -- In case a match is found (so a recce unit name is part of the friendly prefixes), then report that recce to be part of the friendlies.
-- This is important if CAP planes (so planes using their own radar) to be scanning for targets as part of the EWR network. -- This is important if CAP planes (so planes using their own radar) to be scanning for targets as part of the EWR network.
-- But CAP planes are also attackers, so they need to be considered friendlies too! -- But CAP planes are also attackers, so they need to be considered friendlies too!
@@ -1298,7 +1291,7 @@ do -- DETECTION_BASE
end end
end end
self:F( { "Friendlies near Target:", FoundUnitName, FoundUnitCoalition, EnemyUnitName, EnemyCoalition, FoundUnitInReportSetGroup } ) --self:F( { "Friendlies near Target:", FoundUnitName, FoundUnitCoalition, EnemyUnitName, EnemyCoalition, FoundUnitInReportSetGroup } )
if FoundUnitCoalition ~= EnemyCoalition and FoundUnitInReportSetGroup == false then if FoundUnitCoalition ~= EnemyCoalition and FoundUnitInReportSetGroup == false then
local FriendlyUnit = UNIT:Find( FoundDCSUnit ) local FriendlyUnit = UNIT:Find( FoundDCSUnit )
@@ -1313,7 +1306,7 @@ do -- DETECTION_BASE
local Distance = DetectedUnitCoord:Get2DDistance( FriendlyUnit:GetCoordinate() ) local Distance = DetectedUnitCoord:Get2DDistance( FriendlyUnit:GetCoordinate() )
DetectedItem.FriendliesDistance = DetectedItem.FriendliesDistance or {} DetectedItem.FriendliesDistance = DetectedItem.FriendliesDistance or {}
DetectedItem.FriendliesDistance[Distance] = FriendlyUnit DetectedItem.FriendliesDistance[Distance] = FriendlyUnit
self:T( { "Friendlies Found:", FriendlyUnitName = FriendlyUnitName, Distance = Distance, FriendlyUnitCategory = FriendlyUnitCategory, FriendliesCategory = self.FriendliesCategory } ) --self:F( { "Friendlies Found:", FriendlyUnitName = FriendlyUnitName, Distance = Distance, FriendlyUnitCategory = FriendlyUnitCategory, FriendliesCategory = self.FriendliesCategory } )
return true return true
end end
@@ -1355,6 +1348,9 @@ do -- DETECTION_BASE
end end
) )
end end
self:F( { Friendlies = DetectedItem.FriendliesNearBy, Players = DetectedItem.PlayersNearBy } )
end end
end end
@@ -1886,7 +1882,6 @@ do -- DETECTION_UNITS
-- @param #DETECTION_UNITS self -- @param #DETECTION_UNITS self
-- @return #DETECTION_UNITS self -- @return #DETECTION_UNITS self
function DETECTION_UNITS:CreateDetectionItems() function DETECTION_UNITS:CreateDetectionItems()
self:F2( #self.DetectedObjects )
-- Loop the current detected items, and check if each object still exists and is detected. -- Loop the current detected items, and check if each object still exists and is detected.
@@ -2137,7 +2132,6 @@ do -- DETECTION_TYPES
-- @param #DETECTION_TYPES self -- @param #DETECTION_TYPES self
-- @return #DETECTION_TYPES self -- @return #DETECTION_TYPES self
function DETECTION_TYPES:CreateDetectionItems() function DETECTION_TYPES:CreateDetectionItems()
self:F2( #self.DetectedObjects )
-- Loop the current detected items, and check if each object still exists and is detected. -- Loop the current detected items, and check if each object still exists and is detected.
@@ -2525,10 +2519,9 @@ do -- DETECTION_AREAS
-- @param #DETECTION_AREAS self -- @param #DETECTION_AREAS self
-- @return #DETECTION_AREAS self -- @return #DETECTION_AREAS self
function DETECTION_AREAS:CreateDetectionItems() function DETECTION_AREAS:CreateDetectionItems()
self:F2()
self:T( "Checking Detected Items for new Detected Units ..." ) self:T2( "Checking Detected Items for new Detected Units ..." )
-- First go through all detected sets, and check if there are new detected units, match all existing detected units and identify undetected units. -- First go through all detected sets, and check if there are new detected units, match all existing detected units and identify undetected units.
-- Regroup when needed, split groups when needed. -- Regroup when needed, split groups when needed.
for DetectedItemID, DetectedItemData in pairs( self.DetectedItems ) do for DetectedItemID, DetectedItemData in pairs( self.DetectedItems ) do
@@ -2537,8 +2530,7 @@ do -- DETECTION_AREAS
if DetectedItem then if DetectedItem then
self:T( { "Detected Item ID:", DetectedItemID } ) self:T2( { "Detected Item ID: ", DetectedItemID } )
local DetectedSet = DetectedItem.Set local DetectedSet = DetectedItem.Set
@@ -2667,7 +2659,6 @@ do -- DETECTION_AREAS
local DetectedItem = DetectedItemData -- #DETECTION_BASE.DetectedItem local DetectedItem = DetectedItemData -- #DETECTION_BASE.DetectedItem
if DetectedItem then if DetectedItem then
self:T( "Detection Area #" .. DetectedItem.ID )
local DetectedSet = DetectedItem.Set local DetectedSet = DetectedItem.Set
if not self:IsDetectedObjectIdentified( DetectedObject ) and DetectedUnit:IsInZone( DetectedItem.Zone ) then if not self:IsDetectedObjectIdentified( DetectedObject ) and DetectedUnit:IsInZone( DetectedItem.Zone ) then
self:IdentifyDetectedObject( DetectedObject ) self:IdentifyDetectedObject( DetectedObject )

File diff suppressed because it is too large Load Diff

View File

@@ -42,20 +42,21 @@
-- --
-- # Demo Missions -- # Demo Missions
-- --
-- ### [RAT Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/Release/RAT%20-%20Random%20Air%20Traffic) -- ### [MOOSE - ALL Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS)
-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases) -- ### [MOOSE - RAT Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/RAT%20-%20Random%20Air%20Traffic)
-- --
-- === -- ===
-- --
-- # YouTube Channel -- # YouTube Channel
-- --
-- ### [DCS WORLD - MOOSE - RAT - Random Air Traffic](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl0u4Zxywtg-mx_ov4vi68CO) -- ### [MOOSE YouTube Channel](https://www.youtube.com/channel/UCjrA9j5LQoWsG4SpS8i79Qg)
-- ### [MOOSE - RAT - Random Air Traffic](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl0u4Zxywtg-mx_ov4vi68CO)
-- --
-- === -- ===
-- --
-- ### Author: **[funkyfranky](https://forums.eagle.ru/member.php?u=115026)** -- ### Author: **[funkyfranky](https://forums.eagle.ru/member.php?u=115026)**
-- --
-- ### Contributions: **Sven van de Velde ([FlightControl](https://forums.eagle.ru/member.php?u=89536))** -- ### Contributions: [FlightControl](https://forums.eagle.ru/member.php?u=89536)
-- --
-- === -- ===
-- @module Rat -- @module Rat
@@ -116,7 +117,7 @@
-- @field #boolean continuejourney Aircraft will continue their journey, i.e. get respawned at their destination with a new random destination. -- @field #boolean continuejourney Aircraft will continue their journey, i.e. get respawned at their destination with a new random destination.
-- @field #number ngroups Number of groups to be spawned in total. -- @field #number ngroups Number of groups to be spawned in total.
-- @field #number alive Number of groups which are alive. -- @field #number alive Number of groups which are alive.
-- @field #boolean f10menu Add an F10 menu for RAT. -- @field #boolean f10menu If true, add an F10 radiomenu for RAT. Default is false.
-- @field #table Menu F10 menu items for this RAT object. -- @field #table Menu F10 menu items for this RAT object.
-- @field #string SubMenuName Submenu name for RAT object. -- @field #string SubMenuName Submenu name for RAT object.
-- @field #boolean respawn_at_landing Respawn aircraft the moment they land rather than at engine shutdown. -- @field #boolean respawn_at_landing Respawn aircraft the moment they land rather than at engine shutdown.
@@ -350,7 +351,7 @@ RAT={
continuejourney=false, -- Aircraft will continue their journey, i.e. get respawned at their destination with a new random destination. continuejourney=false, -- Aircraft will continue their journey, i.e. get respawned at their destination with a new random destination.
alive=0, -- Number of groups which are alive. alive=0, -- Number of groups which are alive.
ngroups=nil, -- Number of groups to be spawned in total. ngroups=nil, -- Number of groups to be spawned in total.
f10menu=true, -- Add an F10 menu for RAT. f10menu=false, -- Add an F10 menu for RAT.
Menu={}, -- F10 menu items for this RAT object. Menu={}, -- F10 menu items for this RAT object.
SubMenuName=nil, -- Submenu name for RAT object. SubMenuName=nil, -- Submenu name for RAT object.
respawn_at_landing=false, -- Respawn aircraft the moment they land rather than at engine shutdown. respawn_at_landing=false, -- Respawn aircraft the moment they land rather than at engine shutdown.
@@ -506,7 +507,7 @@ RAT.id="RAT | "
--- RAT version. --- RAT version.
-- @list version -- @list version
RAT.version={ RAT.version={
version = "2.2.1", version = "2.2.2",
print = true, print = true,
} }
@@ -1280,7 +1281,7 @@ end
--- Check if aircraft have accidentally been spawned on the runway. If so they will be removed immediatly. --- Check if aircraft have accidentally been spawned on the runway. If so they will be removed immediatly.
-- @param #RAT self -- @param #RAT self
-- @param #booblen switch If true, check is performed. If false, this check is omitted. -- @param #boolean switch If true, check is performed. If false, this check is omitted.
function RAT:CheckOnRunway(switch) function RAT:CheckOnRunway(switch)
self:F2(switch) self:F2(switch)
if switch==nil then if switch==nil then
@@ -1291,7 +1292,7 @@ end
--- Check if aircraft have accidentally been spawned on top of each other. If yes, they will be removed immediately. --- Check if aircraft have accidentally been spawned on top of each other. If yes, they will be removed immediately.
-- @param #RAT self -- @param #RAT self
-- @param #booblen switch If true, check is performed. If false, this check is omitted. -- @param #boolean switch If true, check is performed. If false, this check is omitted.
function RAT:CheckOnTop(switch) function RAT:CheckOnTop(switch)
self:F2(switch) self:F2(switch)
if switch==nil then if switch==nil then
@@ -1302,7 +1303,7 @@ end
--- Put parking spot coordinates in a data base for future use of aircraft. --- Put parking spot coordinates in a data base for future use of aircraft.
-- @param #RAT self -- @param #RAT self
-- @param #booblen switch If true, parking spots are memorized. This is also the default setting. -- @param #boolean switch If true, parking spots are memorized. This is also the default setting.
function RAT:ParkingSpotDB(switch) function RAT:ParkingSpotDB(switch)
self:F2(switch) self:F2(switch)
if switch==nil then if switch==nil then
@@ -1363,6 +1364,21 @@ function RAT:Immortal()
self.immortal=true self.immortal=true
end end
--- Radio menu On. Default is off.
-- @param #RAT self
function RAT:RadioMenuON()
self:F2()
self.f10menu=true
end
--- Radio menu Off. This is the default setting.
-- @param #RAT self
function RAT:RadioMenuOFF()
self:F2()
self.f10menu=false
end
--- Activate uncontrolled aircraft. --- Activate uncontrolled aircraft.
-- @param #RAT self -- @param #RAT self
-- @param #number maxactivated Maximal numnber of activated aircraft. Absolute maximum will be the number of spawned groups. Default is 1. -- @param #number maxactivated Maximal numnber of activated aircraft. Absolute maximum will be the number of spawned groups. Default is 1.
@@ -4265,10 +4281,10 @@ end
function RAT:_CommandImmortal(group, switch) function RAT:_CommandImmortal(group, switch)
-- Command structure for setting groups to invisible. -- Command structure for setting groups to invisible.
local SetInvisible = {id = 'SetImmortal', params = {value = switch}} local SetImmortal = {id = 'SetImmortal', params = {value = switch}}
-- Execute command. -- Execute command.
group:SetCommand(SetInvisible) group:SetCommand(SetImmortal)
end end
--- Adds a parking spot at an airport when it has been used by a spawned RAT aircraft to the RAT parking data base. --- Adds a parking spot at an airport when it has been used by a spawned RAT aircraft to the RAT parking data base.

View File

@@ -32,12 +32,13 @@
-- --
-- # Demo Missions -- # Demo Missions
-- --
-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases) -- ### [MOOSE - ALL Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS)
-- --
-- === -- ===
-- --
-- # YouTube Channel -- # YouTube Channel
-- --
-- ### [MOOSE YouTube Channel](https://www.youtube.com/channel/UCjrA9j5LQoWsG4SpS8i79Qg)
-- ### [MOOSE - On the Range - Demonstration Video](https://www.youtube.com/watch?v=kIXcxNB9_3M) -- ### [MOOSE - On the Range - Demonstration Video](https://www.youtube.com/watch?v=kIXcxNB9_3M)
-- --
-- === -- ===
@@ -211,6 +212,9 @@
-- --
-- The function @{#RANGE.DebugON}() can be used to send messages on screen. It also smokes all defined strafe and bombing targets, the strafe pit approach boxes and the range zone. -- The function @{#RANGE.DebugON}() can be used to send messages on screen. It also smokes all defined strafe and bombing targets, the strafe pit approach boxes and the range zone.
-- --
-- Note that it can happen that the RANGE radio menu is not shown. Check that the range object is defined as a **global** variable rather than a local one.
-- The could avoid the lua garbage collection to accidentally/falsely deallocate the RANGE objects.
--
-- --
-- --
-- @field #RANGE -- @field #RANGE
@@ -279,7 +283,7 @@ RANGE.id="RANGE | "
--- Range script version. --- Range script version.
-- @field #number version -- @field #number version
RANGE.version="1.1.0" RANGE.version="1.1.1"
--TODO list: --TODO list:
--TODO: Add custom weapons, which can be specified by the user. --TODO: Add custom weapons, which can be specified by the user.
@@ -1537,6 +1541,80 @@ function RANGE:_DisplayRangeInfo(_unitname)
end end
end end
--- Display bombing target locations to player.
-- @param #RANGE self
-- @param #string _unitname Name of the player unit.
function RANGE:_DisplayBombTargets(_unitname)
self:F(_unitname)
-- Get player unit and player name.
local _unit, _playername = self:_GetPlayerUnitAndName(_unitname)
-- Check if we have a player.
if _unit and _playername then
-- Player settings.
local _settings=_DATABASE:GetPlayerSettings(_playername) or _SETTINGS --Core.Settings#SETTINGS
-- Message text.
local _text="Bomb Target Locations:"
for _,_bombtarget in pairs(self.bombingTargets) do
local _target=_bombtarget.target --Wrapper.Positionable#POSITIONABLE
if _target and _target:IsAlive() then
-- Core.Point#COORDINATE
local coord=_target:GetCoordinate() --Core.Point#COORDINATE
local mycoord=coord:ToStringA2G(_unit, _settings)
_text=_text..string.format("\n- %s: %s",_bombtarget.name, mycoord)
end
end
self:_DisplayMessageToGroup(_unit,_text, nil, true)
end
end
--- Display pit location and heading to player.
-- @param #RANGE self
-- @param #string _unitname Name of the player unit.
function RANGE:_DisplayStrafePits(_unitname)
self:F(_unitname)
-- Get player unit and player name.
local _unit, _playername = self:_GetPlayerUnitAndName(_unitname)
-- Check if we have a player.
if _unit and _playername then
-- Player settings.
local _settings=_DATABASE:GetPlayerSettings(_playername) or _SETTINGS --Core.Settings#SETTINGS
-- Message text.
local _text="Strafe Target Locations:"
for _,_strafepit in pairs(self.strafeTargets) do
local _target=_strafepit --Wrapper.Positionable#POSITIONABLE
-- Pit parameters.
local coord=_strafepit.coordinate --Core.Point#COORDINATE
local heading=_strafepit.heading
-- Turn heading around ==> approach heading.
if heading>180 then
heading=heading-180
else
heading=heading+180
end
local mycoord=coord:ToStringA2G(_unit, _settings)
_text=_text..string.format("\n- %s: %s - heading %03d",_strafepit.name, mycoord, heading)
end
self:_DisplayMessageToGroup(_unit,_text, nil, true)
end
end
--- Report weather conditions at range. Temperature, QFE pressure and wind data. --- Report weather conditions at range. Temperature, QFE pressure and wind data.
-- @param #RANGE self -- @param #RANGE self
-- @param #string _unitname Name of the player unit. -- @param #string _unitname Name of the player unit.
@@ -1792,11 +1870,11 @@ function RANGE:_AddF10Commands(_unitName)
local _statsPath = missionCommands.addSubMenuForGroup(_gid, "Statistics", _rangePath) local _statsPath = missionCommands.addSubMenuForGroup(_gid, "Statistics", _rangePath)
local _markPath = missionCommands.addSubMenuForGroup(_gid, "Mark Targets", _rangePath) local _markPath = missionCommands.addSubMenuForGroup(_gid, "Mark Targets", _rangePath)
local _settingsPath = missionCommands.addSubMenuForGroup(_gid, "My Settings", _rangePath) local _settingsPath = missionCommands.addSubMenuForGroup(_gid, "My Settings", _rangePath)
local _infoPath = missionCommands.addSubMenuForGroup(_gid, "Range Info", _rangePath)
-- F10/On the Range/<Range Name>/My Settings/ -- F10/On the Range/<Range Name>/My Settings/
local _mysmokePath = missionCommands.addSubMenuForGroup(_gid, "Smoke Color", _settingsPath) local _mysmokePath = missionCommands.addSubMenuForGroup(_gid, "Smoke Color", _settingsPath)
local _myflarePath = missionCommands.addSubMenuForGroup(_gid, "Flare Color", _settingsPath) local _myflarePath = missionCommands.addSubMenuForGroup(_gid, "Flare Color", _settingsPath)
--TODO: Convert to MOOSE menu.
-- F10/On the Range/<Range Name>/Mark Targets/ -- F10/On the Range/<Range Name>/Mark Targets/
missionCommands.addCommandForGroup(_gid, "Mark On Map", _markPath, self._MarkTargetsOnMap, self, _unitName) missionCommands.addCommandForGroup(_gid, "Mark On Map", _markPath, self._MarkTargetsOnMap, self, _unitName)
missionCommands.addCommandForGroup(_gid, "Illuminate Range", _markPath, self._IlluminateBombTargets, self, _unitName) missionCommands.addCommandForGroup(_gid, "Illuminate Range", _markPath, self._IlluminateBombTargets, self, _unitName)
@@ -1824,9 +1902,11 @@ function RANGE:_AddF10Commands(_unitName)
missionCommands.addCommandForGroup(_gid, "Smoke Delay On/Off", _settingsPath, self._SmokeBombDelayOnOff, self, _unitName) missionCommands.addCommandForGroup(_gid, "Smoke Delay On/Off", _settingsPath, self._SmokeBombDelayOnOff, self, _unitName)
missionCommands.addCommandForGroup(_gid, "Smoke Impact On/Off", _settingsPath, self._SmokeBombImpactOnOff, self, _unitName) missionCommands.addCommandForGroup(_gid, "Smoke Impact On/Off", _settingsPath, self._SmokeBombImpactOnOff, self, _unitName)
missionCommands.addCommandForGroup(_gid, "Flare Hits On/Off", _settingsPath, self._FlareDirectHitsOnOff, self, _unitName) missionCommands.addCommandForGroup(_gid, "Flare Hits On/Off", _settingsPath, self._FlareDirectHitsOnOff, self, _unitName)
-- F10/On the Range/<Range Name>/ -- F10/On the Range/<Range Name>/Range Information
missionCommands.addCommandForGroup(_gid, "Range Information", _rangePath, self._DisplayRangeInfo, self, _unitName) missionCommands.addCommandForGroup(_gid, "General Info", _infoPath, self._DisplayRangeInfo, self, _unitName)
missionCommands.addCommandForGroup(_gid, "Weather Report", _rangePath, self._DisplayRangeWeather, self, _unitName) missionCommands.addCommandForGroup(_gid, "Weather Report", _infoPath, self._DisplayRangeWeather, self, _unitName)
missionCommands.addCommandForGroup(_gid, "Bombing Targets", _infoPath, self._DisplayBombTargets, self, _unitName)
missionCommands.addCommandForGroup(_gid, "Strafe Pits", _infoPath, self._DisplayStrafePits, self, _unitName)
end end
else else
self:T(RANGE.id.."Could not find group or group ID in AddF10Menu() function. Unit name: ".._unitName) self:T(RANGE.id.."Could not find group or group ID in AddF10Menu() function. Unit name: ".._unitName)

View File

@@ -858,7 +858,7 @@ function SCORING:OnEventBirth( Event )
if Event.IniUnit then if Event.IniUnit then
if Event.IniObjectCategory == 1 then if Event.IniObjectCategory == 1 then
local PlayerName = Event.IniUnit:GetPlayerName() local PlayerName = Event.IniUnit:GetPlayerName()
if PlayerName ~= "" then if PlayerName then
self:_AddPlayerFromUnit( Event.IniUnit ) self:_AddPlayerFromUnit( Event.IniUnit )
self:SetScoringMenu( Event.IniGroup ) self:SetScoringMenu( Event.IniGroup )
end end

File diff suppressed because it is too large Load Diff

View File

@@ -13,3 +13,5 @@ _DATABASE = DATABASE:New() -- Core.Database#DATABASE
_SETTINGS = SETTINGS:Set() _SETTINGS = SETTINGS:Set()
_SETTINGS:SetPlayerMenuOn() _SETTINGS:SetPlayerMenuOn()
_DATABASE:RegisterCargos()

View File

@@ -86,11 +86,13 @@ COMMANDCENTER = {
-- @return #COMMANDCENTER -- @return #COMMANDCENTER
function COMMANDCENTER:New( CommandCenterPositionable, CommandCenterName ) function COMMANDCENTER:New( CommandCenterPositionable, CommandCenterName )
local self = BASE:Inherit( self, BASE:New() ) local self = BASE:Inherit( self, BASE:New() ) -- #COMMANDCENTER
self.CommandCenterPositionable = CommandCenterPositionable self.CommandCenterPositionable = CommandCenterPositionable
self.CommandCenterName = CommandCenterName or CommandCenterPositionable:GetName() self.CommandCenterName = CommandCenterName or CommandCenterPositionable:GetName()
self.CommandCenterCoalition = CommandCenterPositionable:GetCoalition() self.CommandCenterCoalition = CommandCenterPositionable:GetCoalition()
self.AutoAssignTasks = false
self.Missions = {} self.Missions = {}
@@ -171,7 +173,7 @@ function COMMANDCENTER:New( CommandCenterPositionable, CommandCenterName )
end end
) )
-- Handle when a player leaves a slot and goes back to spectators ... -- Handle when a player crashes ...
-- The PlayerUnit will be UnAssigned from the Task. -- The PlayerUnit will be UnAssigned from the Task.
-- When there is no Unit left running the Task, the Task goes into Abort... -- When there is no Unit left running the Task, the Task goes into Abort...
self:HandleEvent( EVENTS.Crash, self:HandleEvent( EVENTS.Crash,
@@ -220,6 +222,14 @@ function COMMANDCENTER:GetShortText()
end end
--- Gets the coalition of the command center.
-- @param #COMMANDCENTER self
-- @return DCScoalition#coalition
function COMMANDCENTER:GetCoalition()
return self.CommandCenterCoalition
end
--- Gets the POSITIONABLE of the HQ command center. --- Gets the POSITIONABLE of the HQ command center.
-- @param #COMMANDCENTER self -- @param #COMMANDCENTER self
@@ -233,7 +243,7 @@ end
-- @return #list<Tasking.Mission#MISSION> -- @return #list<Tasking.Mission#MISSION>
function COMMANDCENTER:GetMissions() function COMMANDCENTER:GetMissions()
return self.Missions return self.Missions or {}
end end
--- Add a MISSION to be governed by the HQ command center. --- Add a MISSION to be governed by the HQ command center.
@@ -322,7 +332,7 @@ end
--- Sets the menu structure of the Missions governed by the HQ command center. --- Sets the menu structure of the Missions governed by the HQ command center.
-- @param #COMMANDCENTER self -- @param #COMMANDCENTER self
function COMMANDCENTER:SetMenu() function COMMANDCENTER:SetMenu()
self:F() self:F2()
local MenuTime = timer.getTime() local MenuTime = timer.getTime()
for MissionID, Mission in pairs( self:GetMissions() or {} ) do for MissionID, Mission in pairs( self:GetMissions() or {} ) do
@@ -331,7 +341,7 @@ function COMMANDCENTER:SetMenu()
end end
for MissionID, Mission in pairs( self:GetMissions() or {} ) do for MissionID, Mission in pairs( self:GetMissions() or {} ) do
local Mission = Mission -- Tasking.Mission#MISSION Mission = Mission -- Tasking.Mission#MISSION
Mission:RemoveMenu( MenuTime ) Mission:RemoveMenu( MenuTime )
end end
@@ -340,10 +350,133 @@ end
--- Gets the commandcenter menu structure governed by the HQ command center. --- Gets the commandcenter menu structure governed by the HQ command center.
-- @param #COMMANDCENTER self -- @param #COMMANDCENTER self
-- @return Core.Menu#MENU_COALITION -- @return Core.Menu#MENU_COALITION
function COMMANDCENTER:GetMenu() function COMMANDCENTER:GetMenu( TaskGroup )
return self.CommandCenterMenu
local MenuTime = timer.getTime()
self.CommandCenterMenus = self.CommandCenterMenus or {}
local CommandCenterMenu
local CommandCenterText = self:GetText()
CommandCenterMenu = MENU_GROUP:New( TaskGroup, CommandCenterText ):SetTime(MenuTime)
self.CommandCenterMenus[TaskGroup] = CommandCenterMenu
if self.AutoAssignTasks == false then
local AutoAssignTaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, "Assign Task On", CommandCenterMenu, self.SetAutoAssignTasks, self, true ):SetTime(MenuTime):SetTag("AutoTask")
local AssignTaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, "Assign Task", CommandCenterMenu, self.AssignRandomTask, self, TaskGroup ):SetTime(MenuTime):SetTag("AutoTask")
else
local AutoAssignTaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, "Assign Task Off", CommandCenterMenu, self.SetAutoAssignTasks, self, false ):SetTime(MenuTime):SetTag("AutoTask")
end
CommandCenterMenu:Remove( MenuTime, "AutoTask" )
return self.CommandCenterMenus[TaskGroup]
end end
--- Assigns a random task to a TaskGroup.
-- @param #COMMANDCENTER self
-- @return #COMMANDCENTER
function COMMANDCENTER:AssignRandomTask( TaskGroup )
local Tasks = {}
for MissionID, Mission in pairs( self:GetMissions() ) do
local Mission = Mission -- Tasking.Mission#MISSION
local MissionTasks = Mission:GetGroupTasks( TaskGroup )
for MissionTaskName, MissionTask in pairs( MissionTasks or {} ) do
Tasks[#Tasks+1] = MissionTask
end
end
local Task = Tasks[ math.random( 1, #Tasks ) ] -- Tasking.Task#TASK
Task:SetAssignMethod( ACT_ASSIGN_MENU_ACCEPT:New( Task.TaskBriefing ) )
Task:AssignToGroup( TaskGroup )
end
--- Automatically assigns tasks to all TaskGroups.
-- @param #COMMANDCENTER self
-- @param #boolean AutoAssign true for ON and false or nil for OFF.
-- @return #COMMANDCENTER
function COMMANDCENTER:SetAutoAssignTasks( AutoAssign )
self.AutoAssignTasks = AutoAssign or false
local GroupSet = self:AddGroups()
for GroupID, TaskGroup in pairs( GroupSet:GetSet() ) do
local TaskGroup = TaskGroup -- Wrapper.Group#GROUP
self:GetMenu( TaskGroup )
end
if self.AutoAssignTasks == true then
self:ScheduleRepeat( 10, 30, 0, nil, self.AssignTasks, self )
else
self:ScheduleStop( self.AssignTasks )
end
end
--- Automatically assigns tasks to all TaskGroups.
-- @param #COMMANDCENTER self
function COMMANDCENTER:AssignTasks()
local GroupSet = self:AddGroups()
for GroupID, TaskGroup in pairs( GroupSet:GetSet() ) do
local TaskGroup = TaskGroup -- Wrapper.Group#GROUP
if self:IsGroupAssigned( TaskGroup ) then
else
-- Only groups with planes or helicopters will receive automatic tasks.
-- TODO Workaround DCS-BUG-3 - https://github.com/FlightControl-Master/MOOSE/issues/696
if TaskGroup:IsAir() then
self:AssignRandomTask( TaskGroup )
end
end
end
end
--- Get all the Groups active within the command center.
-- @param #COMMANDCENTER self
-- @return Core.Set#SET_GROUP
function COMMANDCENTER:AddGroups()
local GroupSet = SET_GROUP:New()
for MissionID, Mission in pairs( self.Missions ) do
local Mission = Mission -- Tasking.Mission#MISSION
GroupSet = Mission:AddGroups( GroupSet )
end
return GroupSet
end
--- Checks of the TaskGroup has a Task.
-- @param #COMMANDCENTER self
-- @return #boolean
function COMMANDCENTER:IsGroupAssigned( TaskGroup )
local Assigned = false
for MissionID, Mission in pairs( self.Missions ) do
local Mission = Mission -- Tasking.Mission#MISSION
if Mission:IsGroupAssigned( TaskGroup ) then
Assigned = true
break
end
end
return Assigned
end
--- Checks of the COMMANDCENTER has a GROUP. --- Checks of the COMMANDCENTER has a GROUP.
-- @param #COMMANDCENTER self -- @param #COMMANDCENTER self
-- @param Wrapper.Group#GROUP -- @param Wrapper.Group#GROUP
@@ -399,7 +532,7 @@ function COMMANDCENTER:MessageToCoalition( Message )
local CCCoalition = self:GetPositionable():GetCoalition() local CCCoalition = self:GetPositionable():GetCoalition()
--TODO: Fix coalition bug! --TODO: Fix coalition bug!
self:GetPositionable():MessageToCoalition( Message, 15, CCCoalition ) self:GetPositionable():MessageToCoalition( Message, 15, CCCoalition, self:GetShortText() )
end end
@@ -413,7 +546,7 @@ function COMMANDCENTER:MessageTypeToCoalition( Message, MessageType )
local CCCoalition = self:GetPositionable():GetCoalition() local CCCoalition = self:GetPositionable():GetCoalition()
--TODO: Fix coalition bug! --TODO: Fix coalition bug!
self:GetPositionable():MessageTypeToCoalition( Message, MessageType, CCCoalition ) self:GetPositionable():MessageTypeToCoalition( Message, MessageType, CCCoalition, self:GetShortText() )
end end

View File

@@ -29,7 +29,7 @@ MISSION = {
-- @param #string MissionName is the name of the mission. This name will be used to reference the status of each mission by the players. -- @param #string MissionName is the name of the mission. This name will be used to reference the status of each mission by the players.
-- @param #string MissionPriority is a string indicating the "priority" of the Mission. f.e. "Primary", "Secondary" or "First", "Second". It is free format and up to the Mission designer to choose. There are no rules behind this field. -- @param #string MissionPriority is a string indicating the "priority" of the Mission. f.e. "Primary", "Secondary" or "First", "Second". It is free format and up to the Mission designer to choose. There are no rules behind this field.
-- @param #string MissionBriefing is a string indicating the mission briefing to be shown when a player joins a @{CLIENT}. -- @param #string MissionBriefing is a string indicating the mission briefing to be shown when a player joins a @{CLIENT}.
-- @param Dcs.DCSCoalitionWrapper.Object#coalition MissionCoalition is a string indicating the coalition or party to which this mission belongs to. It is free format and can be chosen freely by the mission designer. Note that this field is not to be confused with the coalition concept of the ME. Examples of a Mission Coalition could be "NATO", "CCCP", "Intruders", "Terrorists"... -- @param #string MissionCoalition is a string indicating the coalition or party to which this mission belongs to. It is free format and can be chosen freely by the mission designer. Note that this field is not to be confused with the coalition concept of the ME. Examples of a Mission Coalition could be "NATO", "CCCP", "Intruders", "Terrorists"...
-- @return #MISSION self -- @return #MISSION self
function MISSION:New( CommandCenter, MissionName, MissionPriority, MissionBriefing, MissionCoalition ) function MISSION:New( CommandCenter, MissionName, MissionPriority, MissionBriefing, MissionCoalition )
@@ -265,6 +265,8 @@ function MISSION:New( CommandCenter, MissionName, MissionPriority, MissionBriefi
end end
--- FSM function for a MISSION --- FSM function for a MISSION
-- @param #MISSION self -- @param #MISSION self
-- @param #string From -- @param #string From
@@ -375,24 +377,30 @@ function MISSION:GetScoring()
return self.Scoring return self.Scoring
end end
--- Get the groups for which TASKS are given in the mission --- Gets the groups for which TASKS are given in the mission
-- @param #MISSION self -- @param #MISSION self
-- @param Core.Set#SET_GROUP GroupSet
-- @return Core.Set#SET_GROUP -- @return Core.Set#SET_GROUP
function MISSION:GetGroups() function MISSION:GetGroups()
local SetGroup = SET_GROUP:New() return self:AddGroups()
end
--- Adds the groups for which TASKS are given in the mission
-- @param #MISSION self
-- @param Core.Set#SET_GROUP GroupSet
-- @return Core.Set#SET_GROUP
function MISSION:AddGroups( GroupSet )
GroupSet = GroupSet or SET_GROUP:New()
for TaskID, Task in pairs( self:GetTasks() ) do for TaskID, Task in pairs( self:GetTasks() ) do
local Task = Task -- Tasking.Task#TASK local Task = Task -- Tasking.Task#TASK
local GroupSet = Task:GetGroups() GroupSet = Task:AddGroups( GroupSet )
GroupSet:ForEachGroup(
function( TaskGroup )
SetGroup:Add( TaskGroup, TaskGroup )
end
)
end end
return SetGroup return GroupSet
end end
@@ -441,11 +449,11 @@ do -- Group Assignment
local MissionGroupName = MissionGroup:GetName() local MissionGroupName = MissionGroup:GetName()
if self.AssignedGroups[MissionGroupName] == MissionGroup then if self.AssignedGroups[MissionGroupName] == MissionGroup then
self:T( { "Mission is assigned to:", MissionGroup:GetName() } ) self:T2( { "Mission is assigned to:", MissionGroup:GetName() } )
return true return true
end end
self:T( { "Mission is not assigned to:", MissionGroup:GetName() } ) self:T2( { "Mission is not assigned to:", MissionGroup:GetName() } )
return false return false
end end
@@ -522,18 +530,13 @@ end
function MISSION:GetMenu( TaskGroup ) -- R2.1 -- Changed Menu Structure function MISSION:GetMenu( TaskGroup ) -- R2.1 -- Changed Menu Structure
local CommandCenter = self:GetCommandCenter() local CommandCenter = self:GetCommandCenter()
local CommandCenterMenu = CommandCenter:GetMenu() local CommandCenterMenu = CommandCenter:GetMenu( TaskGroup )
--local MissionMenu = CommandCenterMenu:GetMenu( MissionName )
self.MissionGroupMenu = self.MissionGroupMenu or {} self.MissionGroupMenu = self.MissionGroupMenu or {}
self.MissionGroupMenu[TaskGroup] = self.MissionGroupMenu[TaskGroup] or {} self.MissionGroupMenu[TaskGroup] = self.MissionGroupMenu[TaskGroup] or {}
local GroupMenu = self.MissionGroupMenu[TaskGroup] local GroupMenu = self.MissionGroupMenu[TaskGroup]
local CommandCenterText = CommandCenter:GetText()
CommandCenterMenu = MENU_GROUP:New( TaskGroup, CommandCenterText )
local MissionText = self:GetText() local MissionText = self:GetText()
self.MissionMenu = MENU_GROUP:New( TaskGroup, MissionText, CommandCenterMenu ) self.MissionMenu = MENU_GROUP:New( TaskGroup, MissionText, CommandCenterMenu )
@@ -562,7 +565,7 @@ end
-- @param #string TaskName The Name of the @{Task} within the @{Mission}. -- @param #string TaskName The Name of the @{Task} within the @{Mission}.
-- @return Tasking.Task#TASK The Task -- @return Tasking.Task#TASK The Task
-- @return #nil Returns nil if no task was found. -- @return #nil Returns nil if no task was found.
function MISSION:GetTask( TaskName ) function MISSION:GetTask( TaskName )
self:F( { TaskName } ) self:F( { TaskName } )
return self.Tasks[TaskName] return self.Tasks[TaskName]
@@ -1003,9 +1006,28 @@ end
-- env.info( "Task 2 Completion = " .. Tasks[2]:GetGoalPercentage() .. "%" ) -- env.info( "Task 2 Completion = " .. Tasks[2]:GetGoalPercentage() .. "%" )
function MISSION:GetTasks() function MISSION:GetTasks()
return self.Tasks return self.Tasks or {}
end end
--- Get the relevant tasks of a TaskGroup.
-- @param #MISSION
-- @param Wrapper.Group#GROUP TaskGroup
-- @return #list<Tasking.Task#TASK>
function MISSION:GetGroupTasks( TaskGroup )
local Tasks = {}
for TaskID, Task in pairs( self:GetTasks() ) do
local Task = Task -- Tasking.Task#TASK
if Task:HasGroup( TaskGroup ) then
Tasks[#Tasks+1] = Task
end
end
return Tasks
end
--- Reports the briefing. --- Reports the briefing.
-- @param #MISSION self -- @param #MISSION self
-- @param Wrapper.Group#GROUP ReportGroup The group to which the report needs to be sent. -- @param Wrapper.Group#GROUP ReportGroup The group to which the report needs to be sent.

View File

@@ -161,7 +161,7 @@ TASK = {
-- @return #TASK self -- @return #TASK self
function TASK:New( Mission, SetGroupAssign, TaskName, TaskType, TaskBriefing ) function TASK:New( Mission, SetGroupAssign, TaskName, TaskType, TaskBriefing )
local self = BASE:Inherit( self, FSM_TASK:New() ) -- Tasking.Task#TASK local self = BASE:Inherit( self, FSM_TASK:New( TaskName ) ) -- Tasking.Task#TASK
self:SetStartState( "Planned" ) self:SetStartState( "Planned" )
self:AddTransition( "Planned", "Assign", "Assigned" ) self:AddTransition( "Planned", "Assign", "Assigned" )
@@ -169,10 +169,17 @@ function TASK:New( Mission, SetGroupAssign, TaskName, TaskType, TaskBriefing )
self:AddTransition( "Assigned", "Success", "Success" ) self:AddTransition( "Assigned", "Success", "Success" )
self:AddTransition( "Assigned", "Hold", "Hold" ) self:AddTransition( "Assigned", "Hold", "Hold" )
self:AddTransition( "Assigned", "Fail", "Failed" ) self:AddTransition( "Assigned", "Fail", "Failed" )
self:AddTransition( "Assigned", "Abort", "Aborted" ) self:AddTransition( { "Planned", "Assigned" }, "Abort", "Aborted" )
self:AddTransition( "Assigned", "Cancel", "Cancelled" ) self:AddTransition( "Assigned", "Cancel", "Cancelled" )
self:AddTransition( "Assigned", "Goal", "*" ) self:AddTransition( "Assigned", "Goal", "*" )
self.Fsm = {}
local Fsm = self:GetUnitProcess()
Fsm:SetStartState( "Planned" )
Fsm:AddProcess ( "Planned", "Accept", ACT_ASSIGN_ACCEPT:New( self.TaskBriefing ), { Assigned = "Assigned", Rejected = "Reject" } )
Fsm:AddTransition( "Assigned", "Assigned", "*" )
--- Goal Handler OnBefore for TASK --- Goal Handler OnBefore for TASK
-- @function [parent=#TASK] OnBeforeGoal -- @function [parent=#TASK] OnBeforeGoal
-- @param #TASK self -- @param #TASK self
@@ -209,6 +216,7 @@ function TASK:New( Mission, SetGroupAssign, TaskName, TaskType, TaskBriefing )
self:AddTransition( "*", "PlayerCrashed", "*" ) self:AddTransition( "*", "PlayerCrashed", "*" )
self:AddTransition( "*", "PlayerAborted", "*" ) self:AddTransition( "*", "PlayerAborted", "*" )
self:AddTransition( "*", "PlayerRejected", "*" )
self:AddTransition( "*", "PlayerDead", "*" ) self:AddTransition( "*", "PlayerDead", "*" )
self:AddTransition( { "Failed", "Aborted", "Cancelled" }, "Replan", "Planned" ) self:AddTransition( { "Failed", "Aborted", "Cancelled" }, "Replan", "Planned" )
self:AddTransition( "*", "TimeOut", "Cancelled" ) self:AddTransition( "*", "TimeOut", "Cancelled" )
@@ -216,7 +224,6 @@ function TASK:New( Mission, SetGroupAssign, TaskName, TaskType, TaskBriefing )
self:F( "New TASK " .. TaskName ) self:F( "New TASK " .. TaskName )
self.Processes = {} self.Processes = {}
self.Fsm = {}
self.Mission = Mission self.Mission = Mission
self.CommandCenter = Mission:GetCommandCenter() self.CommandCenter = Mission:GetCommandCenter()
@@ -229,7 +236,6 @@ function TASK:New( Mission, SetGroupAssign, TaskName, TaskType, TaskBriefing )
self:SetBriefing( TaskBriefing ) self:SetBriefing( TaskBriefing )
self.FsmTemplate = self.FsmTemplate or FSM_PROCESS:New()
self.TaskInfo = TASKINFO:New( self ) self.TaskInfo = TASKINFO:New( self )
@@ -246,7 +252,8 @@ function TASK:GetUnitProcess( TaskUnit )
if TaskUnit then if TaskUnit then
return self:GetStateMachine( TaskUnit ) return self:GetStateMachine( TaskUnit )
else else
return self.FsmTemplate self.FsmTemplate = self.FsmTemplate or FSM_PROCESS:New()
return self.FsmTemplate
end end
end end
@@ -295,34 +302,61 @@ function TASK:JoinUnit( PlayerUnit, PlayerGroup )
return PlayerUnitAdded return PlayerUnitAdded
end end
--- Abort a PlayerUnit from a Task. --- A group rejecting a planned task.
-- If the Unit was not part of the Task, false is returned.
-- If the Unit is part of the Task, true is returned.
-- @param #TASK self -- @param #TASK self
-- @param Wrapper.Unit#UNIT PlayerUnit The CLIENT or UNIT of the Player aborting the Task. -- @param Wrapper.Group#GROUP PlayerGroup The group rejecting the task.
-- @return #TASK -- @return #TASK
function TASK:AbortGroup( PlayerGroup ) function TASK:RejectGroup( PlayerGroup )
self:F( { PlayerGroup = PlayerGroup } )
local PlayerGroups = self:GetGroups() local PlayerGroups = self:GetGroups()
-- Is the PlayerGroup part of the PlayerGroups? -- Is the PlayerGroup part of the PlayerGroups?
if PlayerGroups:IsIncludeObject( PlayerGroup ) then if PlayerGroups:IsIncludeObject( PlayerGroup ) then
-- Check if the PlayerGroup is already assigned to the Task. If yes, the PlayerGroup is aborted from the Task. -- Check if the PlayerGroup is already assigned or is planned to be assigned to the Task.
-- If yes, the PlayerGroup is aborted from the Task.
-- If the PlayerUnit was the last unit of the PlayerGroup, the menu needs to be removed from the Group. -- If the PlayerUnit was the last unit of the PlayerGroup, the menu needs to be removed from the Group.
if self:IsStateAssigned() then if self:IsStatePlanned() then
local IsGroupAssigned = self:IsGroupAssigned( PlayerGroup )
if IsGroupAssigned then
local PlayerName = PlayerGroup:GetUnit(1):GetPlayerName()
self:GetMission():GetCommandCenter():MessageToGroup( "Task " .. self:GetName() .. " has been rejected! We will select another task.", PlayerGroup )
self:UnAssignFromGroup( PlayerGroup )
self:PlayerRejected( PlayerGroup:GetUnit(1) )
end
end
end
return self
end
--- A group aborting the task.
-- @param #TASK self
-- @param Wrapper.Group#GROUP PlayerGroup The group aborting the task.
-- @return #TASK
function TASK:AbortGroup( PlayerGroup )
local PlayerGroups = self:GetGroups()
-- Is the PlayerGroup part of the PlayerGroups?
if PlayerGroups:IsIncludeObject( PlayerGroup ) then
-- Check if the PlayerGroup is already assigned or is planned to be assigned to the Task.
-- If yes, the PlayerGroup is aborted from the Task.
-- If the PlayerUnit was the last unit of the PlayerGroup, the menu needs to be removed from the Group.
if self:IsStateAssigned() then
local IsGroupAssigned = self:IsGroupAssigned( PlayerGroup ) local IsGroupAssigned = self:IsGroupAssigned( PlayerGroup )
self:F( { IsGroupAssigned = IsGroupAssigned } )
if IsGroupAssigned then if IsGroupAssigned then
local PlayerName = PlayerGroup:GetUnit(1):GetPlayerName() local PlayerName = PlayerGroup:GetUnit(1):GetPlayerName()
--self:MessageToGroups( PlayerName .. " aborted Task " .. self:GetName() )
self:UnAssignFromGroup( PlayerGroup ) self:UnAssignFromGroup( PlayerGroup )
--self:Abort()
-- Now check if the task needs to go to hold... -- Now check if the task needs to go to hold...
-- It will go to hold, if there are no players in the mission... -- It will go to hold, if there are no players in the mission...
PlayerGroups:Flush( self ) PlayerGroups:Flush( self )
local IsRemaining = false local IsRemaining = false
for GroupName, AssignedGroup in pairs( PlayerGroups:GetSet() or {} ) do for GroupName, AssignedGroup in pairs( PlayerGroups:GetSet() or {} ) do
@@ -347,11 +381,10 @@ function TASK:AbortGroup( PlayerGroup )
return self return self
end end
--- A PlayerUnit crashed in a Task. Abort the Player.
-- If the Unit was not part of the Task, false is returned. --- A group crashing and thus aborting from the task.
-- If the Unit is part of the Task, true is returned.
-- @param #TASK self -- @param #TASK self
-- @param Wrapper.Unit#UNIT PlayerUnit The CLIENT or UNIT of the Player aborting the Task. -- @param Wrapper.Group#GROUP PlayerGroup The group aborting the task.
-- @return #TASK -- @return #TASK
function TASK:CrashGroup( PlayerGroup ) function TASK:CrashGroup( PlayerGroup )
self:F( { PlayerGroup = PlayerGroup } ) self:F( { PlayerGroup = PlayerGroup } )
@@ -413,9 +446,29 @@ end
-- @param #TASK self -- @param #TASK self
-- @return Core.Set#SET_GROUP -- @return Core.Set#SET_GROUP
function TASK:GetGroups() function TASK:GetGroups()
return self.SetGroup return self.SetGroup
end end
--- Gets the SET_GROUP assigned to the TASK.
-- @param #TASK self
-- @param Core.Set#SET_GROUP GroupSet
-- @return Core.Set#SET_GROUP
function TASK:AddGroups( GroupSet )
GroupSet = GroupSet or SET_GROUP:New()
self.SetGroup:ForEachGroup(
--- @param Wrapper.Group#GROUP GroupSet
function( GroupItem )
GroupSet:Add( GroupItem:GetName(), GroupItem)
end
)
return GroupSet
end
do -- Group Assignment do -- Group Assignment
--- Returns if the @{Task} is assigned to the Group. --- Returns if the @{Task} is assigned to the Group.
@@ -500,6 +553,16 @@ end
do -- Group Assignment do -- Group Assignment
--- @param #TASK self
-- @param Actions.Act_Assign#ACT_ASSIGN AcceptClass
function TASK:SetAssignMethod( AcceptClass )
local ProcessTemplate = self:GetUnitProcess()
ProcessTemplate:SetProcess( "Planned", "Accept", AcceptClass ) -- Actions.Act_Assign#ACT_ASSIGN
end
--- Assign the @{Task} to a @{Group}. --- Assign the @{Task} to a @{Group}.
-- @param #TASK self -- @param #TASK self
-- @param Wrapper.Group#GROUP TaskGroup -- @param Wrapper.Group#GROUP TaskGroup
@@ -597,7 +660,9 @@ function TASK:UnAssignFromUnit( TaskUnit )
self:F( TaskUnit:GetName() ) self:F( TaskUnit:GetName() )
self:RemoveStateMachine( TaskUnit ) self:RemoveStateMachine( TaskUnit )
-- If a Task Control Menu had been set, then this will be removed.
self:RemoveTaskControlMenu( TaskUnit )
return self return self
end end
@@ -620,9 +685,11 @@ function TASK:MessageToGroups( Message )
local Mission = self:GetMission() local Mission = self:GetMission()
local CC = Mission:GetCommandCenter() local CC = Mission:GetCommandCenter()
for TaskGroupName, TaskGroup in pairs( self.SetGroup:GetAliveSet() ) do for TaskGroupName, TaskGroup in pairs( self.SetGroup:GetSet() ) do
local TaskGroup = TaskGroup -- Wrapper.Group#GROUP TaskGroup = TaskGroup -- Wrapper.Group#GROUP
CC:MessageToGroup( Message, TaskGroup, TaskGroup:GetName() ) if TaskGroup:IsAlive() == true then
CC:MessageToGroup( Message, TaskGroup, TaskGroup:GetName() )
end
end end
end end
@@ -632,10 +699,11 @@ end
function TASK:SendBriefingToAssignedGroups() function TASK:SendBriefingToAssignedGroups()
self:F2() self:F2()
for TaskGroupName, TaskGroup in pairs( self.SetGroup:GetAliveSet() ) do for TaskGroupName, TaskGroup in pairs( self.SetGroup:GetSet() ) do
if TaskGroup:IsAlive() then
if self:IsGroupAssigned( TaskGroup ) then if self:IsGroupAssigned( TaskGroup ) then
TaskGroup:Message( self.TaskBriefing, 60 ) TaskGroup:Message( self.TaskBriefing, 60 )
end
end end
end end
end end
@@ -646,9 +714,11 @@ end
function TASK:UnAssignFromGroups() function TASK:UnAssignFromGroups()
self:F2() self:F2()
for TaskGroupName, TaskGroup in pairs( self.SetGroup:GetAliveSet() ) do for TaskGroupName, TaskGroup in pairs( self.SetGroup:GetSet() ) do
if self:IsGroupAssigned(TaskGroup) then if TaskGroup:IsAlive() == true then
self:UnAssignFromGroup( TaskGroup ) if self:IsGroupAssigned(TaskGroup) then
self:UnAssignFromGroup( TaskGroup )
end
end end
end end
end end
@@ -661,13 +731,15 @@ end
function TASK:HasAliveUnits() function TASK:HasAliveUnits()
self:F() self:F()
for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetAliveSet() ) do for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do
if self:IsStateAssigned() then if TaskGroup:IsAlive() == true then
if self:IsGroupAssigned( TaskGroup ) then if self:IsStateAssigned() then
for TaskUnitID, TaskUnit in pairs( TaskGroup:GetUnits() ) do if self:IsGroupAssigned( TaskGroup ) then
if TaskUnit:IsAlive() then for TaskUnitID, TaskUnit in pairs( TaskGroup:GetUnits() ) do
self:T( { HasAliveUnits = true } ) if TaskUnit:IsAlive() then
return true self:T( { HasAliveUnits = true } )
return true
end
end end
end end
end end
@@ -686,7 +758,8 @@ function TASK:SetMenu( MenuTime ) --R2.1 Mission Reports and Task Reports added.
self:F( { self:GetName(), MenuTime } ) self:F( { self:GetName(), MenuTime } )
--self.SetGroup:Flush() --self.SetGroup:Flush()
for TaskGroupID, TaskGroupData in pairs( self.SetGroup:GetAliveSet() ) do --for TaskGroupID, TaskGroupData in pairs( self.SetGroup:GetAliveSet() ) do
for TaskGroupID, TaskGroupData in pairs( self.SetGroup:GetSet() ) do
local TaskGroup = TaskGroupData -- Wrapper.Group#GROUP local TaskGroup = TaskGroupData -- Wrapper.Group#GROUP
if TaskGroup:IsAlive() == true and TaskGroup:GetPlayerNames() then if TaskGroup:IsAlive() == true and TaskGroup:GetPlayerNames() then
@@ -729,21 +802,14 @@ function TASK:SetPlannedMenuForGroup( TaskGroup, MenuTime )
local Mission = self:GetMission() local Mission = self:GetMission()
local MissionName = Mission:GetName() local MissionName = Mission:GetName()
local CommandCenter = Mission:GetCommandCenter() local MissionMenu = Mission:GetMenu( TaskGroup )
local CommandCenterMenu = CommandCenter:GetMenu()
local TaskType = self:GetType() local TaskType = self:GetType()
local TaskPlayerCount = self:GetPlayerCount() local TaskPlayerCount = self:GetPlayerCount()
local TaskPlayerString = string.format( " (%dp)", TaskPlayerCount ) local TaskPlayerString = string.format( " (%dp)", TaskPlayerCount )
-- local TaskText = string.format( "%s%s", self:GetName(), TaskPlayerString ) --, TaskThreatLevelString )
local TaskText = string.format( "%s", self:GetName() ) local TaskText = string.format( "%s", self:GetName() )
local TaskName = string.format( "%s", self:GetName() ) local TaskName = string.format( "%s", self:GetName() )
local MissionMenu = Mission:GetMenu( TaskGroup )
--local MissionMenu = MENU_GROUP:New( TaskGroup, MissionName, CommandCenterMenu ):SetTime( MenuTime )
--local MissionMenu = Mission:GetMenu( TaskGroup )
self.MenuPlanned = self.MenuPlanned or {} self.MenuPlanned = self.MenuPlanned or {}
self.MenuPlanned[TaskGroup] = MENU_GROUP_DELAYED:New( TaskGroup, "Join Planned Task", MissionMenu, Mission.MenuReportTasksPerStatus, Mission, TaskGroup, "Planned" ):SetTime( MenuTime ):SetTag( "Tasking" ) self.MenuPlanned[TaskGroup] = MENU_GROUP_DELAYED:New( TaskGroup, "Join Planned Task", MissionMenu, Mission.MenuReportTasksPerStatus, Mission, TaskGroup, "Planned" ):SetTime( MenuTime ):SetTag( "Tasking" )
local TaskTypeMenu = MENU_GROUP_DELAYED:New( TaskGroup, TaskType, self.MenuPlanned[TaskGroup] ):SetTime( MenuTime ):SetTag( "Tasking" ) local TaskTypeMenu = MENU_GROUP_DELAYED:New( TaskGroup, TaskType, self.MenuPlanned[TaskGroup] ):SetTime( MenuTime ):SetTag( "Tasking" )
@@ -768,26 +834,24 @@ end
function TASK:SetAssignedMenuForGroup( TaskGroup, MenuTime ) function TASK:SetAssignedMenuForGroup( TaskGroup, MenuTime )
self:F( { TaskGroup:GetName(), MenuTime } ) self:F( { TaskGroup:GetName(), MenuTime } )
local Mission = self:GetMission()
local MissionName = Mission:GetName()
local CommandCenter = Mission:GetCommandCenter()
local CommandCenterMenu = CommandCenter:GetMenu()
local TaskType = self:GetType() local TaskType = self:GetType()
local TaskPlayerCount = self:GetPlayerCount() local TaskPlayerCount = self:GetPlayerCount()
local TaskPlayerString = string.format( " (%dp)", TaskPlayerCount ) local TaskPlayerString = string.format( " (%dp)", TaskPlayerCount )
local TaskText = string.format( "%s%s", self:GetName(), TaskPlayerString ) --, TaskThreatLevelString ) local TaskText = string.format( "%s%s", self:GetName(), TaskPlayerString ) --, TaskThreatLevelString )
local TaskName = string.format( "%s", self:GetName() ) local TaskName = string.format( "%s", self:GetName() )
local MissionMenu = Mission:GetMenu( TaskGroup ) for UnitName, TaskUnit in pairs( TaskGroup:GetPlayerUnits() ) do
-- local MissionMenu = MENU_GROUP:New( TaskGroup, MissionName, CommandCenterMenu ):SetTime( MenuTime ) local TaskUnit = TaskUnit -- Wrapper.Unit#UNIT
-- local MissionMenu = Mission:GetMenu( TaskGroup ) if TaskUnit then
local MenuControl = self:GetTaskControlMenu( TaskUnit )
self.MenuAssigned = self.MenuAssigned or {} local TaskControl = MENU_GROUP:New( TaskGroup, "Control Task", MenuControl ):SetTime( MenuTime ):SetTag( "Tasking" )
self.MenuAssigned[TaskGroup] = MENU_GROUP_DELAYED:New( TaskGroup, string.format( "Assigned Task %s", TaskName ), MissionMenu ):SetTime( MenuTime ):SetTag( "Tasking" ) if self:IsStateAssigned() then
local TaskMenu = MENU_GROUP_COMMAND_DELAYED:New( TaskGroup, string.format( "Abort Task" ), self.MenuAssigned[TaskGroup], self.MenuTaskAbort, self, TaskGroup ):SetTime( MenuTime ):SetTag( "Tasking" ) local TaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, string.format( "Abort Task" ), TaskControl, self.MenuTaskAbort, self, TaskGroup ):SetTime( MenuTime ):SetTag( "Tasking" )
local MarkMenu = MENU_GROUP_COMMAND_DELAYED:New( TaskGroup, string.format( "Mark Task Location on Map" ), self.MenuAssigned[TaskGroup], self.MenuMarkToGroup, self, TaskGroup ):SetTime( MenuTime ):SetTag( "Tasking" ) end
local TaskTypeMenu = MENU_GROUP_COMMAND_DELAYED:New( TaskGroup, string.format( "Report Task Details" ), self.MenuAssigned[TaskGroup], self.MenuTaskStatus, self, TaskGroup ):SetTime( MenuTime ):SetTag( "Tasking" ) local MarkMenu = MENU_GROUP_COMMAND:New( TaskGroup, string.format( "Mark Task Location on Map" ), TaskControl, self.MenuMarkToGroup, self, TaskGroup ):SetTime( MenuTime ):SetTag( "Tasking" )
local TaskTypeMenu = MENU_GROUP_COMMAND:New( TaskGroup, string.format( "Report Task Details" ), TaskControl, self.MenuTaskStatus, self, TaskGroup ):SetTime( MenuTime ):SetTag( "Tasking" )
end
end
return self return self
end end
@@ -799,10 +863,12 @@ end
function TASK:RemoveMenu( MenuTime ) function TASK:RemoveMenu( MenuTime )
self:F( { self:GetName(), MenuTime } ) self:F( { self:GetName(), MenuTime } )
for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetAliveSet() ) do for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do
local TaskGroup = TaskGroup -- Wrapper.Group#GROUP if TaskGroup:IsAlive() == true then
if TaskGroup:IsAlive() == true and TaskGroup:GetPlayerNames() then local TaskGroup = TaskGroup -- Wrapper.Group#GROUP
self:RefreshMenus( TaskGroup, MenuTime ) if TaskGroup:IsAlive() == true and TaskGroup:GetPlayerNames() then
self:RefreshMenus( TaskGroup, MenuTime )
end
end end
end end
end end
@@ -818,9 +884,6 @@ function TASK:RefreshMenus( TaskGroup, MenuTime )
local Mission = self:GetMission() local Mission = self:GetMission()
local MissionName = Mission:GetName() local MissionName = Mission:GetName()
local CommandCenter = Mission:GetCommandCenter()
local CommandCenterMenu = CommandCenter:GetMenu()
local MissionMenu = Mission:GetMenu( TaskGroup ) local MissionMenu = Mission:GetMenu( TaskGroup )
local TaskName = self:GetName() local TaskName = self:GetName()
@@ -852,7 +915,6 @@ function TASK:RemoveAssignedMenuForGroup( TaskGroup )
local Mission = self:GetMission() local Mission = self:GetMission()
local MissionName = Mission:GetName() local MissionName = Mission:GetName()
local MissionMenu = Mission:GetMenu( TaskGroup ) local MissionMenu = Mission:GetMenu( TaskGroup )
if MissionMenu then if MissionMenu then
@@ -1210,12 +1272,16 @@ function TASK:onenterAssigned( From, Event, To, PlayerUnit, PlayerName )
--- This test is required, because the state transition will be fired also when the state does not change in case of an event. --- This test is required, because the state transition will be fired also when the state does not change in case of an event.
if From ~= "Assigned" then if From ~= "Assigned" then
self:F( { From, Event, To, PlayerUnit:GetName(), PlayerName } )
self:GetMission():GetCommandCenter():MessageToCoalition( "Task " .. self:GetName() .. " is assigned." ) local PlayerNames = self:GetPlayerNames()
local PlayerText = REPORT:New()
for PlayerName, TaskName in pairs( PlayerNames ) do
PlayerText:Add( PlayerName )
end
self:GetMission():GetCommandCenter():MessageToCoalition( "Task " .. self:GetName() .. " is assigned to players " .. PlayerText:Text(",") .. ". Good Luck!" )
-- Set the total Progress to be achieved. -- Set the total Progress to be achieved.
self:SetGoalTotal() -- Polymorphic to set the initial goal total! self:SetGoalTotal() -- Polymorphic to set the initial goal total!
if self.Dispatcher then if self.Dispatcher then
@@ -1231,7 +1297,7 @@ function TASK:onenterAssigned( From, Event, To, PlayerUnit, PlayerName )
self:SetMenu() self:SetMenu()
self:F( { "--> Task Assigned", TaskName = self:GetName(), Mission = self:GetMission():GetName() } ) self:F( { "--> Task Assigned", TaskName = self:GetName(), Mission = self:GetMission():GetName() } )
self:F( { "--> Task Player Names", PlayerNames = self:GetPlayerNames() } ) self:F( { "--> Task Player Names", PlayerNames = PlayerNames } )
end end
end end
@@ -1273,6 +1339,7 @@ function TASK:onenterAborted( From, Event, To )
end end
--- FSM function for a TASK --- FSM function for a TASK
-- @param #TASK self -- @param #TASK self
-- @param #string From -- @param #string From
@@ -1441,11 +1508,13 @@ function TASK:GetPlayerCount() --R2.1 Get a count of the players.
local PlayerCount = 0 local PlayerCount = 0
-- Loop each Unit active in the Task, and find Player Names. -- Loop each Unit active in the Task, and find Player Names.
for TaskGroupID, PlayerGroup in pairs( self:GetGroups():GetAliveSet() ) do for TaskGroupID, PlayerGroup in pairs( self:GetGroups():GetSet() ) do
local PlayerGroup = PlayerGroup -- Wrapper.Group#GROUP local PlayerGroup = PlayerGroup -- Wrapper.Group#GROUP
if self:IsGroupAssigned( PlayerGroup ) then if PlayerGroup:IsAlive() == true then
local PlayerNames = PlayerGroup:GetPlayerNames() if self:IsGroupAssigned( PlayerGroup ) then
PlayerCount = PlayerCount + #PlayerNames local PlayerNames = PlayerGroup:GetPlayerNames()
PlayerCount = PlayerCount + #PlayerNames
end
end end
end end
@@ -1461,12 +1530,14 @@ function TASK:GetPlayerNames() --R2.1 Get a map of the players.
local PlayerNameMap = {} local PlayerNameMap = {}
-- Loop each Unit active in the Task, and find Player Names. -- Loop each Unit active in the Task, and find Player Names.
for TaskGroupID, PlayerGroup in pairs( self:GetGroups():GetAliveSet() ) do for TaskGroupID, PlayerGroup in pairs( self:GetGroups():GetSet() ) do
local PlayerGroup = PlayerGroup -- Wrapper.Group#GROUP local PlayerGroup = PlayerGroup -- Wrapper.Group#GROUP
if self:IsGroupAssigned( PlayerGroup ) then if PlayerGroup:IsAlive() == true then
local PlayerNames = PlayerGroup:GetPlayerNames() if self:IsGroupAssigned( PlayerGroup ) then
for PlayerNameID, PlayerName in pairs( PlayerNames ) do local PlayerNames = PlayerGroup:GetPlayerNames()
PlayerNameMap[PlayerName] = PlayerGroup for PlayerNameID, PlayerName in pairs( PlayerNames ) do
PlayerNameMap[PlayerName] = PlayerGroup
end
end end
end end
end end
@@ -1499,7 +1570,7 @@ function TASK:ReportDetails( ReportGroup )
local PlayerReport = REPORT:New() local PlayerReport = REPORT:New()
for PlayerName, PlayerGroup in pairs( PlayerNames ) do for PlayerName, PlayerGroup in pairs( PlayerNames ) do
PlayerReport:Add( "Group " .. PlayerGroup:GetCallsign() .. ": " .. PlayerName ) PlayerReport:Add( "Players group " .. PlayerGroup:GetCallsign() .. ": " .. PlayerName )
end end
local Players = PlayerReport:Text() local Players = PlayerReport:Text()
@@ -1595,3 +1666,65 @@ do -- Additional Task Scoring and Task Progress
end end
end end
do -- Task Control Menu
-- The Task Control Menu is a menu attached to the task at the main menu to quickly be able to do actions in the task.
-- The Task Control Menu can only be shown when the task is assigned to the player.
-- The Task Control Menu is linked to the process executing the task, so no task menu can be set to the main static task definition.
--- Init Task Control Menu
-- @param #TASK self
-- @param Wrapper.Unit#UNIT TaskUnit The @{Unit} that contains a player.
-- @return Task Control Menu Refresh ID
function TASK:InitTaskControlMenu( TaskUnit )
self.TaskControlMenuTime = timer.getTime()
return self.TaskControlMenuTime
end
--- Get Task Control Menu
-- @param #TASK self
-- @param Wrapper.Unit#UNIT TaskUnit The @{Unit} that contains a player.
-- @return Core.Menu#MENU_GROUP TaskControlMenu The Task Control Menu
function TASK:GetTaskControlMenu( TaskUnit, TaskName )
TaskName = TaskName or ""
local TaskGroup = TaskUnit:GetGroup()
local TaskPlayerCount = TaskGroup:GetPlayerCount()
if TaskPlayerCount <= 1 then
self.TaskControlMenu = MENU_GROUP:New( TaskUnit:GetGroup(), "Task " .. self:GetName() .. " control" ):SetTime( self.TaskControlMenuTime )
else
self.TaskControlMenu = MENU_GROUP:New( TaskUnit:GetGroup(), "Task " .. self:GetName() .. " control for " .. TaskUnit:GetPlayerName() ):SetTime( self.TaskControlMenuTime )
end
return self.TaskControlMenu
end
--- Remove Task Control Menu
-- @param #TASK self
-- @param Wrapper.Unit#UNIT TaskUnit The @{Unit} that contains a player.
function TASK:RemoveTaskControlMenu( TaskUnit )
if self.TaskControlMenu then
self.TaskControlMenu:Remove()
self.TaskControlMenu = nil
end
end
--- Refresh Task Control Menu
-- @param #TASK self
-- @param Wrapper.Unit#UNIT TaskUnit The @{Unit} that contains a player.
-- @param MenuTime The refresh time that was used to refresh the Task Control Menu items.
-- @param MenuTag The tag.
function TASK:RefreshTaskControlMenu( TaskUnit, MenuTime, MenuTag )
if self.TaskControlMenu then
self.TaskControlMenu:Remove( MenuTime, MenuTag )
end
end
end

View File

@@ -249,17 +249,15 @@ end
function TASKINFO:AddCargoSet( SetCargo, Order, Detail, Keep ) function TASKINFO:AddCargoSet( SetCargo, Order, Detail, Keep )
local CargoReport = REPORT:New() local CargoReport = REPORT:New()
CargoReport:Add( "" )
SetCargo:ForEachCargo( SetCargo:ForEachCargo(
--- @param Core.Cargo#CARGO Cargo --- @param Cargo.Cargo#CARGO Cargo
function( Cargo ) function( Cargo )
local CargoType = Cargo:GetType() CargoReport:Add( string.format( ' - %s (%s) %s - status %s ', Cargo:GetName(), Cargo:GetType(), Cargo:GetTransportationMethod(), Cargo:GetCurrentState() ) )
local CargoName = Cargo:GetName()
local CargoCoordinate = Cargo:GetCoordinate()
CargoReport:Add( string.format( '"%s" (%s) at %s', CargoName, CargoType, CargoCoordinate:ToStringMGRS() ) )
end end
) )
self:AddInfo( "CargoSet", CargoReport:Text(), Order, Detail, Keep ) self:AddInfo( "Cargo", CargoReport:Text(), Order, Detail, Keep )
return self return self
end end
@@ -319,7 +317,7 @@ function TASKINFO:Report( Report, Detail, ReportGroup )
local Coordinate = Data.Data -- Core.Point#COORDINATE local Coordinate = Data.Data -- Core.Point#COORDINATE
Text = Coordinate:ToStringWind( ReportGroup:GetUnit(1), nil, self ) Text = Coordinate:ToStringWind( ReportGroup:GetUnit(1), nil, self )
end end
if Key == "CargoSet" then if Key == "Cargo" then
local DataText = Data.Data -- #string local DataText = Data.Data -- #string
Text = DataText Text = DataText
end end

View File

@@ -62,8 +62,6 @@ do -- TASK_A2A
local Fsm = self:GetUnitProcess() local Fsm = self:GetUnitProcess()
Fsm:AddProcess ( "Planned", "Accept", ACT_ASSIGN_ACCEPT:New( self.TaskBriefing ), { Assigned = "RouteToRendezVous", Rejected = "Reject" } )
Fsm:AddTransition( "Assigned", "RouteToRendezVous", "RoutingToRendezVous" ) Fsm:AddTransition( "Assigned", "RouteToRendezVous", "RoutingToRendezVous" )
Fsm:AddProcess ( "RoutingToRendezVous", "RouteToRendezVousPoint", ACT_ROUTE_POINT:New(), { Arrived = "ArriveAtRendezVous" } ) Fsm:AddProcess ( "RoutingToRendezVous", "RouteToRendezVousPoint", ACT_ROUTE_POINT:New(), { Arrived = "ArriveAtRendezVous" } )
Fsm:AddProcess ( "RoutingToRendezVous", "RouteToRendezVousZone", ACT_ROUTE_ZONE:New(), { Arrived = "ArriveAtRendezVous" } ) Fsm:AddProcess ( "RoutingToRendezVous", "RouteToRendezVousZone", ACT_ROUTE_ZONE:New(), { Arrived = "ArriveAtRendezVous" } )
@@ -84,6 +82,15 @@ do -- TASK_A2A
Fsm:AddTransition( "Rejected", "Reject", "Aborted" ) Fsm:AddTransition( "Rejected", "Reject", "Aborted" )
Fsm:AddTransition( "Failed", "Fail", "Failed" ) Fsm:AddTransition( "Failed", "Fail", "Failed" )
---- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit
-- @param #TASK_CARGO Task
function Fsm:OnLeaveAssigned( TaskUnit, Task )
self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
self:SelectAction()
end
--- Test --- Test
-- @param #FSM_PROCESS self -- @param #FSM_PROCESS self

View File

@@ -61,9 +61,6 @@ do -- TASK_A2G
local Fsm = self:GetUnitProcess() local Fsm = self:GetUnitProcess()
Fsm:AddProcess ( "Planned", "Accept", ACT_ASSIGN_ACCEPT:New( self.TaskBriefing ), { Assigned = "RouteToRendezVous", Rejected = "Reject" } )
Fsm:AddTransition( "Assigned", "RouteToRendezVous", "RoutingToRendezVous" ) Fsm:AddTransition( "Assigned", "RouteToRendezVous", "RoutingToRendezVous" )
Fsm:AddProcess ( "RoutingToRendezVous", "RouteToRendezVousPoint", ACT_ROUTE_POINT:New(), { Arrived = "ArriveAtRendezVous" } ) Fsm:AddProcess ( "RoutingToRendezVous", "RouteToRendezVousPoint", ACT_ROUTE_POINT:New(), { Arrived = "ArriveAtRendezVous" } )
Fsm:AddProcess ( "RoutingToRendezVous", "RouteToRendezVousZone", ACT_ROUTE_ZONE:New(), { Arrived = "ArriveAtRendezVous" } ) Fsm:AddProcess ( "RoutingToRendezVous", "RouteToRendezVousZone", ACT_ROUTE_ZONE:New(), { Arrived = "ArriveAtRendezVous" } )
@@ -84,6 +81,18 @@ do -- TASK_A2G
Fsm:AddTransition( "Rejected", "Reject", "Aborted" ) Fsm:AddTransition( "Rejected", "Reject", "Aborted" )
Fsm:AddTransition( "Failed", "Fail", "Failed" ) Fsm:AddTransition( "Failed", "Fail", "Failed" )
--- Test
-- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit
-- @param Tasking.Task_A2G#TASK_A2G Task
function Fsm:onafterAssigned( TaskUnit, Task )
self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
-- Determine the first Unit from the self.RendezVousSetUnit
self:RouteToRendezVous()
end
--- Test --- Test
-- @param #FSM_PROCESS self -- @param #FSM_PROCESS self

View File

@@ -13,6 +13,7 @@
-- The following classes are important to consider: -- The following classes are important to consider:
-- --
-- * @{#TASK_CARGO_TRANSPORT}: Defines a task for a human player to transport a set of cargo between various zones. -- * @{#TASK_CARGO_TRANSPORT}: Defines a task for a human player to transport a set of cargo between various zones.
-- * @{#TASK_CARGO_CSAR}: Defines a task for a human player to Search and Rescue wounded pilots.
-- --
-- === -- ===
-- --
@@ -22,7 +23,7 @@
-- --
-- === -- ===
-- --
-- @module Task_Cargo -- @module Tasking.Task_Cargo
do -- TASK_CARGO do -- TASK_CARGO
@@ -166,24 +167,69 @@ do -- TASK_CARGO
self.DeployZones = {} -- setmetatable( {}, { __mode = "v" } ) -- weak table on value self.DeployZones = {} -- setmetatable( {}, { __mode = "v" } ) -- weak table on value
self:AddTransition( "*", "CargoDeployed", "*" )
--- CargoDeployed Handler OnBefore for TASK_CARGO
-- @function [parent=#TASK_CARGO] OnBeforeCargoDeployed
-- @param #TASK_CARGO self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that Deployed the cargo. You can use this to retrieve the PlayerName etc.
-- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status.
-- @param Core.Zone#ZONE DeployZone The zone where the Cargo got Deployed or UnBoarded.
-- @return #boolean
--- CargoDeployed Handler OnAfter for TASK_CARGO
-- @function [parent=#TASK_CARGO] OnAfterCargoDeployed
-- @param #TASK_CARGO self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that Deployed the cargo. You can use this to retrieve the PlayerName etc.
-- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status.
-- @param Core.Zone#ZONE DeployZone The zone where the Cargo got Deployed or UnBoarded.
self:AddTransition( "*", "CargoPickedUp", "*" )
--- CargoPickedUp Handler OnBefore for TASK_CARGO
-- @function [parent=#TASK_CARGO] OnBeforeCargoPickedUp
-- @param #TASK_CARGO self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that PickedUp the cargo. You can use this to retrieve the PlayerName etc.
-- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status.
-- @return #boolean
--- CargoPickedUp Handler OnAfter for TASK_CARGO
-- @function [parent=#TASK_CARGO] OnAfterCargoPickedUp
-- @param #TASK_CARGO self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that PickedUp the cargo. You can use this to retrieve the PlayerName etc.
-- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status.
local Fsm = self:GetUnitProcess() local Fsm = self:GetUnitProcess()
Fsm:SetStartState( "Planned" ) -- Fsm:SetStartState( "Planned" )
--
Fsm:AddProcess ( "Planned", "Accept", ACT_ASSIGN_ACCEPT:New( self.TaskBriefing ), { Assigned = "SelectAction", Rejected = "Reject" } ) -- Fsm:AddProcess ( "Planned", "Accept", ACT_ASSIGN_ACCEPT:New( self.TaskBriefing ), { Assigned = "SelectAction", Rejected = "Reject" } )
Fsm:AddTransition( { "Planned", "Assigned", "WaitingForCommand", "ArrivedAtPickup", "ArrivedAtDeploy", "Boarded", "UnBoarded", "Landed", "Boarding" }, "SelectAction", "*" ) Fsm:AddTransition( { "Planned", "Assigned", "Cancelled", "WaitingForCommand", "ArrivedAtPickup", "ArrivedAtDeploy", "Boarded", "UnBoarded", "Loaded", "UnLoaded", "Landed", "Boarding" }, "SelectAction", "*" )
Fsm:AddTransition( "*", "RouteToPickup", "RoutingToPickup" ) Fsm:AddTransition( "*", "RouteToPickup", "RoutingToPickup" )
Fsm:AddProcess ( "RoutingToPickup", "RouteToPickupPoint", ACT_ROUTE_POINT:New(), { Arrived = "ArriveAtPickup", Cancelled = "CancelRouteToPickup" } ) Fsm:AddProcess ( "RoutingToPickup", "RouteToPickupPoint", ACT_ROUTE_POINT:New(), { Arrived = "ArriveAtPickup", Cancelled = "CancelRouteToPickup" } )
Fsm:AddTransition( "Arrived", "ArriveAtPickup", "ArrivedAtPickup" ) Fsm:AddTransition( "Arrived", "ArriveAtPickup", "ArrivedAtPickup" )
Fsm:AddTransition( "Cancelled", "CancelRouteToPickup", "WaitingForCommand" ) Fsm:AddTransition( "Cancelled", "CancelRouteToPickup", "Cancelled" )
Fsm:AddTransition( "*", "RouteToDeploy", "RoutingToDeploy" ) Fsm:AddTransition( "*", "RouteToDeploy", "RoutingToDeploy" )
Fsm:AddProcess ( "RoutingToDeploy", "RouteToDeployZone", ACT_ROUTE_ZONE:New(), { Arrived = "ArriveAtDeploy", Cancelled = "CancelRouteToDeploy" } ) Fsm:AddProcess ( "RoutingToDeploy", "RouteToDeployZone", ACT_ROUTE_ZONE:New(), { Arrived = "ArriveAtDeploy", Cancelled = "CancelRouteToDeploy" } )
Fsm:AddTransition( "Arrived", "ArriveAtDeploy", "ArrivedAtDeploy" ) Fsm:AddTransition( "Arrived", "ArriveAtDeploy", "ArrivedAtDeploy" )
Fsm:AddTransition( "Cancelled", "CancelRouteToDeploy", "WaitingForCommand" ) Fsm:AddTransition( "Cancelled", "CancelRouteToDeploy", "Cancelled" )
Fsm:AddTransition( { "ArrivedAtPickup", "ArrivedAtDeploy", "Landing" }, "Land", "Landing" ) Fsm:AddTransition( { "ArrivedAtPickup", "ArrivedAtDeploy", "Landing" }, "Land", "Landing" )
Fsm:AddTransition( "Landing", "Landed", "Landed" ) Fsm:AddTransition( "Landing", "Landed", "Landed" )
@@ -191,10 +237,14 @@ do -- TASK_CARGO
Fsm:AddTransition( "*", "PrepareBoarding", "AwaitBoarding" ) Fsm:AddTransition( "*", "PrepareBoarding", "AwaitBoarding" )
Fsm:AddTransition( "AwaitBoarding", "Board", "Boarding" ) Fsm:AddTransition( "AwaitBoarding", "Board", "Boarding" )
Fsm:AddTransition( "Boarding", "Boarded", "Boarded" ) Fsm:AddTransition( "Boarding", "Boarded", "Boarded" )
Fsm:AddTransition( "*", "Load", "Loaded" )
Fsm:AddTransition( "*", "PrepareUnBoarding", "AwaitUnBoarding" ) Fsm:AddTransition( "*", "PrepareUnBoarding", "AwaitUnBoarding" )
Fsm:AddTransition( "AwaitUnBoarding", "UnBoard", "UnBoarding" ) Fsm:AddTransition( "AwaitUnBoarding", "UnBoard", "UnBoarding" )
Fsm:AddTransition( "UnBoarding", "UnBoarded", "UnBoarded" ) Fsm:AddTransition( "UnBoarding", "UnBoarded", "UnBoarded" )
Fsm:AddTransition( "*", "Unload", "Unloaded" )
Fsm:AddTransition( "*", "Planned", "Planned" ) Fsm:AddTransition( "*", "Planned", "Planned" )
@@ -202,30 +252,33 @@ do -- TASK_CARGO
Fsm:AddTransition( "Deployed", "Success", "Success" ) Fsm:AddTransition( "Deployed", "Success", "Success" )
Fsm:AddTransition( "Rejected", "Reject", "Aborted" ) Fsm:AddTransition( "Rejected", "Reject", "Aborted" )
Fsm:AddTransition( "Failed", "Fail", "Failed" ) Fsm:AddTransition( "Failed", "Fail", "Failed" )
---- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit
-- @param #TASK_CARGO Task
function Fsm:OnAfterAssigned( TaskUnit, Task )
self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
self:SelectAction()
end
--- ---
-- @param #FSM_PROCESS self -- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit -- @param Wrapper.Unit#UNIT TaskUnit
-- @param Tasking.Task_CARGO#TASK_CARGO Task -- @param #TASK_CARGO Task
function Fsm:onafterSelectAction( TaskUnit, Task ) function Fsm:onafterSelectAction( TaskUnit, Task )
local TaskUnitName = TaskUnit:GetName() local TaskUnitName = TaskUnit:GetName()
local MenuTime = Task:InitTaskControlMenu( TaskUnit )
self:F( { TaskUnit = TaskUnitName, Task = Task and Task:GetClassNameAndID() } ) local MenuControl = Task:GetTaskControlMenu( TaskUnit )
local MenuTime = timer.getTime()
TaskUnit.Menu = MENU_GROUP:New( TaskUnit:GetGroup(), Task:GetName() .. " @ " .. TaskUnit:GetName() )
local CargoItemCount = TaskUnit:CargoItemCount() local CargoItemCount = TaskUnit:CargoItemCount()
--Task:GetMission():GetCommandCenter():MessageToGroup( "Cargo in carrier: " .. CargoItemCount, TaskUnit:GetGroup() )
Task.SetCargo:ForEachCargo( Task.SetCargo:ForEachCargo(
--- @param Core.Cargo#CARGO Cargo --- @param Cargo.Cargo#CARGO Cargo
function( Cargo ) function( Cargo )
if Cargo:IsAlive() then if Cargo:IsAlive() then
@@ -234,18 +287,20 @@ do -- TASK_CARGO
-- MENU_GROUP_COMMAND:New( -- MENU_GROUP_COMMAND:New(
-- TaskUnit:GetGroup(), -- TaskUnit:GetGroup(),
-- "Cancel Route " .. Cargo.Name, -- "Cancel Route " .. Cargo.Name,
-- TaskUnit.Menu, -- MenuControl,
-- self.MenuRouteToPickupCancel, -- self.MenuRouteToPickupCancel,
-- self, -- self,
-- Cargo -- Cargo
-- ):SetTime(MenuTime) -- ):SetTime(MenuTime)
-- end -- end
self:F( { CargoUnloaded = Cargo:IsUnLoaded(), CargoLoaded = Cargo:IsLoaded(), CargoItemCount = CargoItemCount } ) --self:F( { CargoUnloaded = Cargo:IsUnLoaded(), CargoLoaded = Cargo:IsLoaded(), CargoItemCount = CargoItemCount } )
local TaskGroup = TaskUnit:GetGroup()
if Cargo:IsUnLoaded() then if Cargo:IsUnLoaded() then
if CargoItemCount <= Task.CargoLimit then if CargoItemCount < 1 then
if Cargo:IsInRadius( TaskUnit:GetPointVec2() ) then if Cargo:IsInReportRadius( TaskUnit:GetPointVec2() ) then
local NotInDeployZones = true local NotInDeployZones = true
for DeployZoneName, DeployZone in pairs( Task.DeployZones ) do for DeployZoneName, DeployZone in pairs( Task.DeployZones ) do
if Cargo:IsInZone( DeployZone ) then if Cargo:IsInZone( DeployZone ) then
@@ -254,28 +309,82 @@ do -- TASK_CARGO
end end
if NotInDeployZones then if NotInDeployZones then
if not TaskUnit:InAir() then if not TaskUnit:InAir() then
MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), "Board cargo " .. Cargo.Name, TaskUnit.Menu, self.MenuBoardCargo, self, Cargo ):SetTime(MenuTime) if Cargo:CanBoard() == true then
TaskUnit.Menu:SetTime( MenuTime ) if Cargo:IsInLoadRadius( TaskUnit:GetPointVec2() ) then
Cargo:Report( "Ready for boarding.", "board", TaskUnit:GetGroup() )
local BoardMenu = MENU_GROUP:New( TaskGroup, "Board cargo", MenuControl ):SetTime( MenuTime ):SetTag( "Cargo" )
MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), Cargo.Name, BoardMenu, self.MenuBoardCargo, self, Cargo ):SetTime(MenuTime):SetTag("Cargo"):SetRemoveParent()
else
Cargo:Report( "Board at " .. Cargo:GetCoordinate():ToString( TaskUnit:GetGroup() .. "." ), "reporting", TaskUnit:GetGroup() )
end
else
if Cargo:CanLoad() == true then
if Cargo:IsInLoadRadius( TaskUnit:GetPointVec2() ) then
Cargo:Report( "Ready for loading.", "load", TaskUnit:GetGroup() )
local LoadMenu = MENU_GROUP:New( TaskGroup, "Load cargo", MenuControl ):SetTime( MenuTime ):SetTag( "Cargo" )
MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), Cargo.Name, LoadMenu, self.MenuLoadCargo, self, Cargo ):SetTime(MenuTime):SetTag("Cargo"):SetRemoveParent()
else
Cargo:Report( "Load at " .. Cargo:GetCoordinate():ToString( TaskUnit:GetGroup() ) .. " within " .. Cargo.NearRadius .. ".", "reporting", TaskUnit:GetGroup() )
end
else
if Cargo:CanSlingload() == true then
if Cargo:IsInLoadRadius( TaskUnit:GetPointVec2() ) then
Cargo:Report( "Ready for slingloading.", "slingload", TaskUnit:GetGroup() )
else
Cargo:Report( "Slingload at " .. Cargo:GetCoordinate():ToString( TaskUnit:GetGroup() ) .. ".", "reporting", TaskUnit:GetGroup() )
end
end
end
end
else
Cargo:ReportResetAll( TaskUnit:GetGroup() )
end end
end end
else else
MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), "Route to Pickup cargo " .. Cargo.Name, TaskUnit.Menu, self.MenuRouteToPickup, self, Cargo ):SetTime(MenuTime) if not Cargo:IsDeployed() == true then
TaskUnit.Menu:SetTime( MenuTime ) local RouteToPickupMenu = MENU_GROUP:New( TaskGroup, "Route to pickup cargo", MenuControl ):SetTime( MenuTime ):SetTag( "Cargo" )
MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), Cargo.Name, RouteToPickupMenu, self.MenuRouteToPickup, self, Cargo ):SetTime(MenuTime):SetTag("Cargo"):SetRemoveParent()
Cargo:ReportResetAll( TaskUnit:GetGroup() )
end
end
end
-- Cargo in deployzones are flagged as deployed.
for DeployZoneName, DeployZone in pairs( Task.DeployZones ) do
if Cargo:IsInZone( DeployZone ) then
Task:E( { CargoIsDeployed = Task.CargoDeployed and "true" or "false" } )
if Cargo:IsDeployed() == false then
Cargo:SetDeployed( true )
-- Now we call a callback method to handle the CargoDeployed event.
Task:E( { CargoIsAlive = Cargo:IsAlive() and "true" or "false" } )
if Cargo:IsAlive() then
Task:CargoDeployed( TaskUnit, Cargo, DeployZone )
end
end
end
end
end
if Cargo:IsLoaded() == true and Cargo:IsLoadedInCarrier( TaskUnit ) == true then
if not TaskUnit:InAir() then
if Cargo:CanUnboard() == true then
local UnboardMenu = MENU_GROUP:New( TaskGroup, "Unboard cargo", MenuControl ):SetTime( MenuTime ):SetTag( "Cargo" )
MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), Cargo.Name, UnboardMenu, self.MenuUnboardCargo, self, Cargo ):SetTime(MenuTime):SetTag("Cargo"):SetRemoveParent()
else
if Cargo:CanUnload() == true then
local UnloadMenu = MENU_GROUP:New( TaskGroup, "Unload cargo", MenuControl ):SetTime( MenuTime ):SetTag( "Cargo" )
MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), Cargo.Name, UnloadMenu, self.MenuUnloadCargo, self, Cargo ):SetTime(MenuTime):SetTag("Cargo"):SetRemoveParent()
end
end end
end end
end end
if Cargo:IsLoaded() then -- Deployzones are optional zones that can be selected to request routing information.
if not TaskUnit:InAir() then for DeployZoneName, DeployZone in pairs( Task.DeployZones ) do
MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), "Unboard cargo " .. Cargo.Name, TaskUnit.Menu, self.MenuUnBoardCargo, self, Cargo ):SetTime(MenuTime) if not Cargo:IsInZone( DeployZone ) then
TaskUnit.Menu:SetTime( MenuTime ) local RouteToDeployMenu = MENU_GROUP:New( TaskGroup, "Route to deploy cargo", MenuControl ):SetTime( MenuTime ):SetTag( "Cargo" )
end MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), "Zone " .. DeployZoneName, RouteToDeployMenu, self.MenuRouteToDeploy, self, DeployZone ):SetTime(MenuTime):SetTag("Cargo"):SetRemoveParent()
-- Deployzones are optional zones that can be selected to request routing information.
for DeployZoneName, DeployZone in pairs( Task.DeployZones ) do
if not Cargo:IsInZone( DeployZone ) then
MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), "Route to Deploy cargo at " .. DeployZoneName, TaskUnit.Menu, self.MenuRouteToDeploy, self, DeployZone ):SetTime(MenuTime)
TaskUnit.Menu:SetTime( MenuTime )
end
end end
end end
end end
@@ -283,10 +392,9 @@ do -- TASK_CARGO
end end
) )
TaskUnit.Menu:Remove( MenuTime ) Task:RefreshTaskControlMenu( TaskUnit, MenuTime, "Cargo" )
self:__SelectAction( -1 )
self:__SelectAction( -15 )
end end
@@ -294,21 +402,31 @@ do -- TASK_CARGO
--- ---
-- @param #FSM_PROCESS self -- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit -- @param Wrapper.Unit#UNIT TaskUnit
-- @param Tasking.Task_Cargo#TASK_CARGO Task -- @param #TASK_CARGO Task
function Fsm:OnLeaveWaitingForCommand( TaskUnit, Task ) function Fsm:OnLeaveWaitingForCommand( TaskUnit, Task )
self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
TaskUnit.Menu:Remove() --local MenuControl = Task:GetTaskControlMenu( TaskUnit )
--MenuControl:Remove()
end end
function Fsm:MenuBoardCargo( Cargo ) function Fsm:MenuBoardCargo( Cargo )
self:__PrepareBoarding( 1.0, Cargo ) self:__PrepareBoarding( 1.0, Cargo )
end end
function Fsm:MenuUnBoardCargo( Cargo, DeployZone ) function Fsm:MenuLoadCargo( Cargo )
self:__Load( 1.0, Cargo )
end
function Fsm:MenuUnboardCargo( Cargo, DeployZone )
self:__PrepareUnBoarding( 1.0, Cargo, DeployZone ) self:__PrepareUnBoarding( 1.0, Cargo, DeployZone )
end end
function Fsm:MenuUnloadCargo( Cargo, DeployZone )
self:__Unload( 1.0, Cargo, DeployZone )
end
function Fsm:MenuRouteToPickup( Cargo ) function Fsm:MenuRouteToPickup( Cargo )
self:__RouteToPickup( 1.0, Cargo ) self:__RouteToPickup( 1.0, Cargo )
end end
@@ -335,7 +453,7 @@ do -- TASK_CARGO
self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
if Cargo:IsAlive() then if Cargo:IsAlive() then
self.Cargo = Cargo -- Core.Cargo#CARGO self.Cargo = Cargo -- Cargo.Cargo#CARGO
Task:SetCargoPickup( self.Cargo, TaskUnit ) Task:SetCargoPickup( self.Cargo, TaskUnit )
self:__RouteToPickupPoint( -0.1 ) self:__RouteToPickupPoint( -0.1 )
end end
@@ -350,7 +468,6 @@ do -- TASK_CARGO
function Fsm:onafterArriveAtPickup( TaskUnit, Task ) function Fsm:onafterArriveAtPickup( TaskUnit, Task )
self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
if self.Cargo:IsAlive() then if self.Cargo:IsAlive() then
self.Cargo:Smoke( Task:GetSmokeColor(), 15 )
if TaskUnit:IsAir() then if TaskUnit:IsAir() then
Task:GetMission():GetCommandCenter():MessageToGroup( "Land", TaskUnit:GetGroup() ) Task:GetMission():GetCommandCenter():MessageToGroup( "Land", TaskUnit:GetGroup() )
self:__Land( -0.1, "Pickup" ) self:__Land( -0.1, "Pickup" )
@@ -367,6 +484,7 @@ do -- TASK_CARGO
function Fsm:onafterCancelRouteToPickup( TaskUnit, Task ) function Fsm:onafterCancelRouteToPickup( TaskUnit, Task )
self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
Task:GetMission():GetCommandCenter():MessageToGroup( "Cancelled routing to Cargo " .. self.Cargo:GetName(), TaskUnit:GetGroup() )
self:__SelectAction( -0.1 ) self:__SelectAction( -0.1 )
end end
@@ -404,6 +522,7 @@ do -- TASK_CARGO
function Fsm:onafterCancelRouteToDeploy( TaskUnit, Task ) function Fsm:onafterCancelRouteToDeploy( TaskUnit, Task )
self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
Task:GetMission():GetCommandCenter():MessageToGroup( "Cancelled routing to deploy zone " .. self.DeployZone:GetName(), TaskUnit:GetGroup() )
self:__SelectAction( -0.1 ) self:__SelectAction( -0.1 )
end end
@@ -415,19 +534,30 @@ do -- TASK_CARGO
function Fsm:onafterLand( TaskUnit, Task, From, Event, To, Action ) function Fsm:onafterLand( TaskUnit, Task, From, Event, To, Action )
self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
if self.Cargo:IsAlive() then if Action == "Pickup" then
if self.Cargo:IsInRadius( TaskUnit:GetPointVec2() ) then if self.Cargo:IsAlive() then
if TaskUnit:InAir() then if self.Cargo:IsInReportRadius( TaskUnit:GetPointVec2() ) then
self:__Land( -10, Action ) if TaskUnit:InAir() then
self:__Land( -10, Action )
else
Task:GetMission():GetCommandCenter():MessageToGroup( "Landed at pickup location...", TaskUnit:GetGroup() )
self:__Landed( -0.1, Action )
end
else else
Task:GetMission():GetCommandCenter():MessageToGroup( "Landed ...", TaskUnit:GetGroup() ) self:__RouteToPickup( -0.1, self.Cargo )
self:__Landed( -0.1, Action )
end end
else end
if Action == "Pickup" then else
self:__RouteToPickupZone( -0.1 ) if TaskUnit:IsAlive() then
if TaskUnit:IsInZone( self.DeployZone ) then
if TaskUnit:InAir() then
self:__Land( -10, Action )
else
Task:GetMission():GetCommandCenter():MessageToGroup( "Landed at deploy zone " .. self.DeployZone:GetName(), TaskUnit:GetGroup() )
self:__Landed( -0.1, Action )
end
else else
self:__RouteToDeployZone( -0.1 ) self:__RouteToDeploy( -0.1, self.Cargo )
end end
end end
end end
@@ -439,18 +569,28 @@ do -- TASK_CARGO
function Fsm:onafterLanded( TaskUnit, Task, From, Event, To, Action ) function Fsm:onafterLanded( TaskUnit, Task, From, Event, To, Action )
self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
if self.Cargo:IsAlive() then if Action == "Pickup" then
if self.Cargo:IsInRadius( TaskUnit:GetPointVec2() ) then if self.Cargo:IsAlive() then
if TaskUnit:InAir() then if self.Cargo:IsInReportRadius( TaskUnit:GetPointVec2() ) then
self:__Land( -0.1, Action ) if TaskUnit:InAir() then
self:__Land( -0.1, Action )
else
self:__SelectAction( -0.1 )
end
else else
self:__SelectAction( -0.1 ) self:__RouteToPickup( -0.1, self.Cargo )
end end
else end
if Action == "Pickup" then else
self:__RouteToPickupZone( -0.1 ) if TaskUnit:IsAlive() then
if TaskUnit:IsInZone( self.DeployZone ) then
if TaskUnit:InAir() then
self:__Land( -10, Action )
else
self:__SelectAction( -0.1 )
end
else else
self:__RouteToDeployZone( -0.1 ) self:__RouteToDeploy( -0.1, self.Cargo )
end end
end end
end end
@@ -463,30 +603,30 @@ do -- TASK_CARGO
self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
if Cargo and Cargo:IsAlive() then if Cargo and Cargo:IsAlive() then
self.Cargo = Cargo -- Core.Cargo#CARGO_GROUP self:__Board( -0.1, Cargo )
self:__Board( -0.1 )
end end
end end
--- @param #FSM_PROCESS self --- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit -- @param Wrapper.Unit#UNIT TaskUnit
-- @param Tasking.Task_Cargo#TASK_CARGO Task -- @param Tasking.Task_Cargo#TASK_CARGO Task
function Fsm:onafterBoard( TaskUnit, Task ) function Fsm:onafterBoard( TaskUnit, Task, From, Event, To, Cargo )
self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
function self.Cargo:OnEnterLoaded( From, Event, To, TaskUnit, TaskProcess ) function Cargo:OnEnterLoaded( From, Event, To, TaskUnit, TaskProcess )
self:F({From, Event, To, TaskUnit, TaskProcess }) self:F({From, Event, To, TaskUnit, TaskProcess })
TaskProcess:__Boarded( 0.1 ) TaskProcess:__Boarded( 0.1, self )
end end
if self.Cargo:IsAlive() then if Cargo:IsAlive() then
if self.Cargo:IsInRadius( TaskUnit:GetPointVec2() ) then if Cargo:IsInLoadRadius( TaskUnit:GetPointVec2() ) then
if TaskUnit:InAir() then if TaskUnit:InAir() then
--- ABORT the boarding. Split group if any and go back to select action. --- ABORT the boarding. Split group if any and go back to select action.
else else
self.Cargo:MessageToGroup( "Boarding ...", TaskUnit:GetGroup() ) Cargo:MessageToGroup( "Boarding ...", TaskUnit:GetGroup() )
if not self.Cargo:IsBoarding() then if not Cargo:IsBoarding() then
self.Cargo:Board( TaskUnit, 20, self ) Cargo:Board( TaskUnit, 20, self )
end end
end end
else else
@@ -499,24 +639,36 @@ do -- TASK_CARGO
--- @param #FSM_PROCESS self --- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit -- @param Wrapper.Unit#UNIT TaskUnit
-- @param Tasking.Task_Cargo#TASK_CARGO Task -- @param Tasking.Task_Cargo#TASK_CARGO Task
function Fsm:onafterBoarded( TaskUnit, Task ) function Fsm:onafterBoarded( TaskUnit, Task, From, Event, To, Cargo )
local TaskUnitName = TaskUnit:GetName()
self:F( { TaskUnit = TaskUnitName, Task = Task and Task:GetClassNameAndID() } )
Cargo:MessageToGroup( "Boarded cargo " .. Cargo:GetName(), TaskUnit:GetGroup() )
self:__Load( -0.1, Cargo )
end
--- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit
-- @param Tasking.Task_Cargo#TASK_CARGO Task
function Fsm:onafterLoad( TaskUnit, Task, From, Event, To, Cargo )
local TaskUnitName = TaskUnit:GetName() local TaskUnitName = TaskUnit:GetName()
self:F( { TaskUnit = TaskUnitName, Task = Task and Task:GetClassNameAndID() } ) self:F( { TaskUnit = TaskUnitName, Task = Task and Task:GetClassNameAndID() } )
self.Cargo:MessageToGroup( "Boarded ...", TaskUnit:GetGroup() ) if not Cargo:IsLoaded() then
Cargo:Load( TaskUnit )
TaskUnit:AddCargo( self.Cargo )
self:__SelectAction( 1 )
-- TODO:I need to find a more decent solution for this.
Task:E( { CargoPickedUp = Task.CargoPickedUp } )
if self.Cargo:IsAlive() then
if Task.CargoPickedUp then
Task:CargoPickedUp( TaskUnit, self.Cargo )
end
end end
Cargo:MessageToGroup( "Loaded cargo " .. Cargo:GetName(), TaskUnit:GetGroup() )
TaskUnit:AddCargo( Cargo )
Task:CargoPickedUp( TaskUnit, Cargo )
self:SelectAction( -1 )
end end
@@ -530,7 +682,7 @@ do -- TASK_CARGO
-- @param To -- @param To
-- @param Cargo -- @param Cargo
-- @param Core.Zone#ZONE_BASE DeployZone -- @param Core.Zone#ZONE_BASE DeployZone
function Fsm:onafterPrepareUnBoarding( TaskUnit, Task, From, Event, To, Cargo ) function Fsm:onafterPrepareUnBoarding( TaskUnit, Task, From, Event, To, Cargo )
self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID(), From, Event, To, Cargo } ) self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID(), From, Event, To, Cargo } )
self.Cargo = Cargo self.Cargo = Cargo
@@ -568,9 +720,9 @@ do -- TASK_CARGO
if self.Cargo:IsAlive() then if self.Cargo:IsAlive() then
self.Cargo:MessageToGroup( "UnBoarding ...", TaskUnit:GetGroup() ) self.Cargo:MessageToGroup( "UnBoarding ...", TaskUnit:GetGroup() )
if DeployZone then if DeployZone then
self.Cargo:UnBoard( DeployZone:GetPointVec2(), 400, self ) self.Cargo:UnBoard( DeployZone:GetCoordinate():GetRandomCoordinateInRadius( 25, 10 ), 400, self )
else else
self.Cargo:UnBoard( TaskUnit:GetPointVec2():AddX(60), 400, self ) self.Cargo:UnBoard( TaskUnit:GetCoordinate():GetRandomCoordinateInRadius( 25, 10 ), 400, self )
end end
end end
end end
@@ -585,34 +737,34 @@ do -- TASK_CARGO
local TaskUnitName = TaskUnit:GetName() local TaskUnitName = TaskUnit:GetName()
self:F( { TaskUnit = TaskUnitName, Task = Task and Task:GetClassNameAndID() } ) self:F( { TaskUnit = TaskUnitName, Task = Task and Task:GetClassNameAndID() } )
self.Cargo:MessageToGroup( "UnBoarded ...", TaskUnit:GetGroup() ) self.Cargo:MessageToGroup( "UnBoarded cargo " .. self.Cargo:GetName(), TaskUnit:GetGroup() )
TaskUnit:RemoveCargo( self.Cargo )
local NotInDeployZones = true
for DeployZoneName, DeployZone in pairs( Task.DeployZones ) do
if self.Cargo:IsInZone( DeployZone ) then
NotInDeployZones = false
end
end
if NotInDeployZones == false then self:Unload( self.Cargo )
self.Cargo:SetDeployed( true ) end
end
---
-- TODO:I need to find a more decent solution for this. -- @param #FSM_PROCESS self
Task:E( { CargoDeployed = Task.CargoDeployed and "true" or "false" } ) -- @param Wrapper.Unit#UNIT TaskUnit
Task:E( { CargoIsAlive = self.Cargo:IsAlive() and "true" or "false" } ) -- @param Tasking.Task_Cargo#TASK_CARGO Task
if self.Cargo:IsAlive() then function Fsm:onafterUnload( TaskUnit, Task, From, Event, To, Cargo, DeployZone )
if Task.CargoDeployed then
Task:CargoDeployed( TaskUnit, self.Cargo, self.DeployZone ) local TaskUnitName = TaskUnit:GetName()
end self:F( { TaskUnit = TaskUnitName, Task = Task and Task:GetClassNameAndID() } )
end
if not Cargo:IsUnLoaded() then
if DeployZone then
Cargo:UnLoad( DeployZone:GetCoordinate():GetRandomCoordinateInRadius( 25, 10 ), 400, self )
else
Cargo:UnLoad( TaskUnit:GetCoordinate():GetRandomCoordinateInRadius( 25, 10 ), 400, self )
end
end
TaskUnit:RemoveCargo( Cargo )
Cargo:MessageToGroup( "Unloaded cargo " .. Cargo:GetName(), TaskUnit:GetGroup() )
self:Planned() self:Planned()
self:__SelectAction( 1 ) self:__SelectAction( 1 )
end end
return self return self
@@ -675,13 +827,17 @@ do -- TASK_CARGO
self:F({Cargo, TaskUnit}) self:F({Cargo, TaskUnit})
local ProcessUnit = self:GetUnitProcess( TaskUnit ) local ProcessUnit = self:GetUnitProcess( TaskUnit )
local MenuTime = self:InitTaskControlMenu( TaskUnit )
local MenuControl = self:GetTaskControlMenu( TaskUnit )
local ActRouteCargo = ProcessUnit:GetProcess( "RoutingToPickup", "RouteToPickupPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT local ActRouteCargo = ProcessUnit:GetProcess( "RoutingToPickup", "RouteToPickupPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT
ActRouteCargo:Reset() ActRouteCargo:Reset()
ActRouteCargo:SetCoordinate( Cargo:GetCoordinate() ) ActRouteCargo:SetCoordinate( Cargo:GetCoordinate() )
ActRouteCargo:SetRange( Cargo:GetBoardingRange() ) ActRouteCargo:SetRange( Cargo:GetLoadRadius() )
ActRouteCargo:SetMenuCancel( TaskUnit:GetGroup(), "Cancel Routing to Cargo " .. Cargo:GetName(), TaskUnit.Menu ) ActRouteCargo:SetMenuCancel( TaskUnit:GetGroup(), "Cancel Routing to Cargo " .. Cargo:GetName(), MenuControl, MenuTime, "Cargo" )
ActRouteCargo:Start() ActRouteCargo:Start()
return self return self
end end
@@ -694,11 +850,15 @@ do -- TASK_CARGO
local ProcessUnit = self:GetUnitProcess( TaskUnit ) local ProcessUnit = self:GetUnitProcess( TaskUnit )
local MenuTime = self:InitTaskControlMenu( TaskUnit )
local MenuControl = self:GetTaskControlMenu( TaskUnit )
local ActRouteDeployZone = ProcessUnit:GetProcess( "RoutingToDeploy", "RouteToDeployZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE local ActRouteDeployZone = ProcessUnit:GetProcess( "RoutingToDeploy", "RouteToDeployZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE
ActRouteDeployZone:Reset() ActRouteDeployZone:Reset()
ActRouteDeployZone:SetZone( DeployZone ) ActRouteDeployZone:SetZone( DeployZone )
ActRouteDeployZone:SetMenuCancel( TaskUnit:GetGroup(), "Cancel Routing to Deploy Zone" .. DeployZone:GetName(), TaskUnit.Menu ) ActRouteDeployZone:SetMenuCancel( TaskUnit:GetGroup(), "Cancel Routing to Deploy Zone" .. DeployZone:GetName(), MenuControl, MenuTime, "Cargo" )
ActRouteDeployZone:Start() ActRouteDeployZone:Start()
return self return self
end end
@@ -726,12 +886,12 @@ do -- TASK_CARGO
end end
--- @param #TASK_CARGO self --- @param #TASK_CARGO self
-- @param @list<Core.Zone#ZONE> DeployZones -- @param #list<Core.Zone#ZONE> DeployZones
-- @param Wrapper.Unit#UNIT TaskUnit -- @param Wrapper.Unit#UNIT TaskUnit
-- @return #TASK_CARGO -- @return #TASK_CARGO
function TASK_CARGO:SetDeployZones( DeployZones, TaskUnit ) function TASK_CARGO:SetDeployZones( DeployZones, TaskUnit )
for DeployZoneID, DeployZone in pairs( DeployZones ) do for DeployZoneID, DeployZone in pairs( DeployZones or {} ) do
self.DeployZones[DeployZone:GetName()] = DeployZone self.DeployZones[DeployZone:GetName()] = DeployZone
end end
@@ -813,7 +973,6 @@ do -- TASK_CARGO
function TASK_CARGO:UpdateTaskInfo( DetectedItem ) function TASK_CARGO:UpdateTaskInfo( DetectedItem )
if self:IsStatePlanned() or self:IsStateAssigned() then if self:IsStatePlanned() or self:IsStateAssigned() then
self.TaskInfo:AddTaskName( 0, "MSOD" )
self.TaskInfo:AddCargoSet( self.SetCargo, 10, "SOD", true ) self.TaskInfo:AddCargoSet( self.SetCargo, 10, "SOD", true )
end end
end end
@@ -823,186 +982,8 @@ do -- TASK_CARGO
return 0 return 0
end end
end end
do -- TASK_CARGO_TRANSPORT
--- The TASK_CARGO_TRANSPORT class
-- @type TASK_CARGO_TRANSPORT
-- @extends #TASK_CARGO
TASK_CARGO_TRANSPORT = {
ClassName = "TASK_CARGO_TRANSPORT",
}
--- Instantiates a new TASK_CARGO_TRANSPORT.
-- @param #TASK_CARGO_TRANSPORT self
-- @param Tasking.Mission#MISSION Mission
-- @param Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned.
-- @param #string TaskName The name of the Task.
-- @param Core.Set#SET_CARGO SetCargo The scope of the cargo to be transported.
-- @param #string TaskBriefing The Cargo Task briefing.
-- @return #TASK_CARGO_TRANSPORT self
function TASK_CARGO_TRANSPORT:New( Mission, SetGroup, TaskName, SetCargo, TaskBriefing )
local self = BASE:Inherit( self, TASK_CARGO:New( Mission, SetGroup, TaskName, SetCargo, "Transport", TaskBriefing ) ) -- #TASK_CARGO_TRANSPORT
self:F()
Mission:AddTask( self )
-- Events
self:AddTransition( "*", "CargoPickedUp", "*" )
self:AddTransition( "*", "CargoDeployed", "*" )
self:F( { CargoDeployed = self.CargoDeployed ~= nil and "true" or "false" } )
--- OnBefore Transition Handler for Event CargoPickedUp.
-- @function [parent=#TASK_CARGO_TRANSPORT] OnBeforeCargoPickedUp
-- @param #TASK_CARGO_TRANSPORT self
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that PickedUp the cargo. You can use this to retrieve the PlayerName etc.
-- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event CargoPickedUp.
-- @function [parent=#TASK_CARGO_TRANSPORT] OnAfterCargoPickedUp
-- @param #TASK_CARGO_TRANSPORT self
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that PickedUp the cargo. You can use this to retrieve the PlayerName etc.
-- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status.
--- Synchronous Event Trigger for Event CargoPickedUp.
-- @function [parent=#TASK_CARGO_TRANSPORT] CargoPickedUp
-- @param #TASK_CARGO_TRANSPORT self
-- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that PickedUp the cargo. You can use this to retrieve the PlayerName etc.
-- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status.
--- Asynchronous Event Trigger for Event CargoPickedUp.
-- @function [parent=#TASK_CARGO_TRANSPORT] __CargoPickedUp
-- @param #TASK_CARGO_TRANSPORT self
-- @param #number Delay The delay in seconds.
-- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that PickedUp the cargo. You can use this to retrieve the PlayerName etc.
-- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status.
--- OnBefore Transition Handler for Event CargoDeployed.
-- @function [parent=#TASK_CARGO_TRANSPORT] OnBeforeCargoDeployed
-- @param #TASK_CARGO_TRANSPORT self
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that Deployed the cargo. You can use this to retrieve the PlayerName etc.
-- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status.
-- @param Core.Zone#ZONE DeployZone The zone where the Cargo got Deployed or UnBoarded.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event CargoDeployed.
-- @function [parent=#TASK_CARGO_TRANSPORT] OnAfterCargoDeployed
-- @param #TASK_CARGO_TRANSPORT self
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that Deployed the cargo. You can use this to retrieve the PlayerName etc.
-- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status.
-- @param Core.Zone#ZONE DeployZone The zone where the Cargo got Deployed or UnBoarded.
--- Synchronous Event Trigger for Event CargoDeployed.
-- @function [parent=#TASK_CARGO_TRANSPORT] CargoDeployed
-- @param #TASK_CARGO_TRANSPORT self
-- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that Deployed the cargo. You can use this to retrieve the PlayerName etc.
-- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status.
-- @param Core.Zone#ZONE DeployZone The zone where the Cargo got Deployed or UnBoarded.
--- Asynchronous Event Trigger for Event CargoDeployed.
-- @function [parent=#TASK_CARGO_TRANSPORT] __CargoDeployed
-- @param #TASK_CARGO_TRANSPORT self
-- @param #number Delay The delay in seconds.
-- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that Deployed the cargo. You can use this to retrieve the PlayerName etc.
-- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status.
-- @param Core.Zone#ZONE DeployZone The zone where the Cargo got Deployed or UnBoarded.
local Fsm = self:GetUnitProcess()
local CargoReport = REPORT:New( "Transport Cargo. The following cargo needs to be transported including initial positions:")
SetCargo:ForEachCargo(
--- @param Core.Cargo#CARGO Cargo
function( Cargo )
local CargoType = Cargo:GetType()
local CargoName = Cargo:GetName()
local CargoCoordinate = Cargo:GetCoordinate()
CargoReport:Add( string.format( '- "%s" (%s) at %s', CargoName, CargoType, CargoCoordinate:ToStringMGRS() ) )
end
)
self:SetBriefing(
TaskBriefing or
CargoReport:Text()
)
return self
end
function TASK_CARGO_TRANSPORT:ReportOrder( ReportGroup )
return 0
end
---
-- @param #TASK_CARGO_TRANSPORT self
-- @return #boolean
function TASK_CARGO_TRANSPORT:IsAllCargoTransported()
local CargoSet = self:GetCargoSet()
local Set = CargoSet:GetSet()
local DeployZones = self:GetDeployZones()
local CargoDeployed = true
-- Loop the CargoSet (so evaluate each Cargo in the SET_CARGO ).
for CargoID, CargoData in pairs( Set ) do
local Cargo = CargoData -- Core.Cargo#CARGO
self:F( { Cargo = Cargo:GetName(), CargoDeployed = Cargo:IsDeployed() } )
if Cargo:IsDeployed() then
-- -- Loop the DeployZones set for the TASK_CARGO_TRANSPORT.
-- for DeployZoneID, DeployZone in pairs( DeployZones ) do
--
-- -- If all cargo is in one of the deploy zones, then all is good.
-- self:T( { Cargo.CargoObject } )
-- if Cargo:IsInZone( DeployZone ) == false then
-- CargoDeployed = false
-- end
-- end
else
CargoDeployed = false
end
end
self:F( { CargoDeployed = CargoDeployed } )
return CargoDeployed
end
--- @param #TASK_CARGO_TRANSPORT self
function TASK_CARGO_TRANSPORT:onafterGoal( TaskUnit, From, Event, To )
local CargoSet = self.CargoSet
if self:IsAllCargoTransported() then
self:Success()
end
self:__Goal( -10 )
end
end

View File

@@ -0,0 +1,187 @@
--- **Tasking** -- Models tasks for players to execute CSAR @{Cargo} downed pilots.
--
-- ![Banner Image](..\Presentations\TASK_CARGO\Dia1.JPG)
--
-- ===
do -- TASK_CARGO_CSAR
--- The TASK_CARGO_CSAR class
-- @type TASK_CARGO_CSAR
-- @extends Tasking.Task_Cargo#TASK_CARGO
TASK_CARGO_CSAR = {
ClassName = "TASK_CARGO_CSAR",
}
--- Instantiates a new TASK_CARGO_CSAR.
-- @param #TASK_CARGO_CSAR self
-- @param Tasking.Mission#MISSION Mission
-- @param Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned.
-- @param #string TaskName The name of the Task.
-- @param Core.Set#SET_CARGO SetCargo The scope of the cargo to be transported.
-- @param #string TaskBriefing The Cargo Task briefing.
-- @return #TASK_CARGO_CSAR self
function TASK_CARGO_CSAR:New( Mission, SetGroup, TaskName, SetCargo, TaskBriefing )
local self = BASE:Inherit( self, TASK_CARGO:New( Mission, SetGroup, TaskName, SetCargo, "CSAR", TaskBriefing ) ) -- #TASK_CARGO_CSAR
self:F()
Mission:AddTask( self )
-- Events
self:AddTransition( "*", "CargoPickedUp", "*" )
self:AddTransition( "*", "CargoDeployed", "*" )
self:F( { CargoDeployed = self.CargoDeployed ~= nil and "true" or "false" } )
--- OnBefore Transition Handler for Event CargoPickedUp.
-- @function [parent=#TASK_CARGO_CSAR] OnBeforeCargoPickedUp
-- @param #TASK_CARGO_CSAR self
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that PickedUp the cargo. You can use this to retrieve the PlayerName etc.
-- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event CargoPickedUp.
-- @function [parent=#TASK_CARGO_CSAR] OnAfterCargoPickedUp
-- @param #TASK_CARGO_CSAR self
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that PickedUp the cargo. You can use this to retrieve the PlayerName etc.
-- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status.
--- Synchronous Event Trigger for Event CargoPickedUp.
-- @function [parent=#TASK_CARGO_CSAR] CargoPickedUp
-- @param #TASK_CARGO_CSAR self
-- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that PickedUp the cargo. You can use this to retrieve the PlayerName etc.
-- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status.
--- Asynchronous Event Trigger for Event CargoPickedUp.
-- @function [parent=#TASK_CARGO_CSAR] __CargoPickedUp
-- @param #TASK_CARGO_CSAR self
-- @param #number Delay The delay in seconds.
-- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that PickedUp the cargo. You can use this to retrieve the PlayerName etc.
-- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status.
--- OnBefore Transition Handler for Event CargoDeployed.
-- @function [parent=#TASK_CARGO_CSAR] OnBeforeCargoDeployed
-- @param #TASK_CARGO_CSAR self
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that Deployed the cargo. You can use this to retrieve the PlayerName etc.
-- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status.
-- @param Core.Zone#ZONE DeployZone The zone where the Cargo got Deployed or UnBoarded.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event CargoDeployed.
-- @function [parent=#TASK_CARGO_CSAR] OnAfterCargoDeployed
-- @param #TASK_CARGO_CSAR self
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that Deployed the cargo. You can use this to retrieve the PlayerName etc.
-- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status.
-- @param Core.Zone#ZONE DeployZone The zone where the Cargo got Deployed or UnBoarded.
--- Synchronous Event Trigger for Event CargoDeployed.
-- @function [parent=#TASK_CARGO_CSAR] CargoDeployed
-- @param #TASK_CARGO_CSAR self
-- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that Deployed the cargo. You can use this to retrieve the PlayerName etc.
-- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status.
-- @param Core.Zone#ZONE DeployZone The zone where the Cargo got Deployed or UnBoarded.
--- Asynchronous Event Trigger for Event CargoDeployed.
-- @function [parent=#TASK_CARGO_CSAR] __CargoDeployed
-- @param #TASK_CARGO_CSAR self
-- @param #number Delay The delay in seconds.
-- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that Deployed the cargo. You can use this to retrieve the PlayerName etc.
-- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status.
-- @param Core.Zone#ZONE DeployZone The zone where the Cargo got Deployed or UnBoarded.
local Fsm = self:GetUnitProcess()
local CargoReport = REPORT:New( "Rescue a downed pilot from the following position:")
SetCargo:ForEachCargo(
--- @param Core.Cargo#CARGO Cargo
function( Cargo )
local CargoType = Cargo:GetType()
local CargoName = Cargo:GetName()
local CargoCoordinate = Cargo:GetCoordinate()
CargoReport:Add( string.format( '- "%s" (%s) at %s', CargoName, CargoType, CargoCoordinate:ToStringMGRS() ) )
end
)
self:SetBriefing(
TaskBriefing or
CargoReport:Text()
)
return self
end
function TASK_CARGO_CSAR:ReportOrder( ReportGroup )
return 0
end
---
-- @param #TASK_CARGO_CSAR self
-- @return #boolean
function TASK_CARGO_CSAR:IsAllCargoTransported()
local CargoSet = self:GetCargoSet()
local Set = CargoSet:GetSet()
local DeployZones = self:GetDeployZones()
local CargoDeployed = true
-- Loop the CargoSet (so evaluate each Cargo in the SET_CARGO ).
for CargoID, CargoData in pairs( Set ) do
local Cargo = CargoData -- Core.Cargo#CARGO
self:F( { Cargo = Cargo:GetName(), CargoDeployed = Cargo:IsDeployed() } )
if Cargo:IsDeployed() then
-- -- Loop the DeployZones set for the TASK_CARGO_CSAR.
-- for DeployZoneID, DeployZone in pairs( DeployZones ) do
--
-- -- If all cargo is in one of the deploy zones, then all is good.
-- self:T( { Cargo.CargoObject } )
-- if Cargo:IsInZone( DeployZone ) == false then
-- CargoDeployed = false
-- end
-- end
else
CargoDeployed = false
end
end
self:F( { CargoDeployed = CargoDeployed } )
return CargoDeployed
end
--- @param #TASK_CARGO_CSAR self
function TASK_CARGO_CSAR:onafterGoal( TaskUnit, From, Event, To )
local CargoSet = self.CargoSet
if self:IsAllCargoTransported() then
self:Success()
end
self:__Goal( -10 )
end
end

View File

@@ -0,0 +1,601 @@
--- **Tasking** - Creates and manages player TASK_CARGO tasks.
--
-- ===
--
-- ### Author: **FlightControl**
--
-- ### Contributions:
--
-- ===
--
-- @module Task_Cargo_Dispatcher
do -- TASK_CARGO_DISPATCHER
--- TASK_CARGO_DISPATCHER class.
-- @type TASK_CARGO_DISPATCHER
-- @extends Tasking.Task_Manager#TASK_MANAGER
-- @field TASK_CARGO_DISPATCHER.CSAR CSAR
--- @type TASK_CARGO_DISPATCHER.CSAR
-- @field Wrapper.Unit#UNIT PilotUnit
-- @field Tasking.Task#TASK Task
--- # TASK_CARGO_DISPATCHER class, extends @{Task_Manager#TASK_MANAGER}
--
-- ![Banner Image](..\Presentations\TASK_CARGO_DISPATCHER\Dia1.JPG)
--
-- The @{#TASK_CARGO_DISPATCHER} class implements the dynamic dispatching of cargo tasks.
--
-- ![Banner Image](..\Presentations\TASK_CARGO_DISPATCHER\Dia3.JPG)
--
-- The EWR will detect units, will group them, and will dispatch @{Task}s to groups. Depending on the type of target detected, different tasks will be dispatched.
-- Find a summary below describing for which situation a task type is created:
--
-- ![Banner Image](..\Presentations\TASK_CARGO_DISPATCHER\Dia9.JPG)
--
-- * **CSAR Task**: Is created when a fiendly pilot has ejected from a plane, and needs to be rescued (sometimes behind enemy lines).
--
-- ## 1. TASK\_A2A\_DISPATCHER constructor:
--
-- The @{#TASK_CARGO_DISPATCHER.New}() method creates a new TASK\_A2A\_DISPATCHER instance.
--
-- ### 1.1. Define or set the **Mission**:
--
-- Tasking is executed to accomplish missions. Therefore, a MISSION object needs to be given as the first parameter.
--
-- local HQ = GROUP:FindByName( "HQ", "Bravo" )
-- local CommandCenter = COMMANDCENTER:New( HQ, "Lima" )
-- local Mission = MISSION:New( CommandCenter, "A2A Mission", "High", "Watch the air enemy units being detected.", coalition.side.RED )
--
-- Missions are governed by COMMANDCENTERS, so, ensure you have a COMMANDCENTER object installed and setup within your mission.
-- Create the MISSION object, and hook it under the command center.
--
-- ### 1.2. Build a set of the groups seated by human players:
--
-- ![Banner Image](..\Presentations\TASK_CARGO_DISPATCHER\Dia6.JPG)
--
-- A set or collection of the groups wherein human players can be seated, these can be clients or units that can be joined as a slot or jumping into.
--
-- local AttackGroups = SET_GROUP:New():FilterCoalitions( "red" ):FilterPrefixes( "Defender" ):FilterStart()
--
-- The set is built using the SET_GROUP class. Apply any filter criteria to identify the correct groups for your mission.
-- Only these slots or units will be able to execute the mission and will receive tasks for this mission, once available.
--
-- ### 1.3. Define the **EWR network**:
--
-- As part of the TASK\_A2A\_DISPATCHER constructor, an EWR network must be given as the third parameter.
-- An EWR network, or, Early Warning Radar network, is used to early detect potential airborne targets and to understand the position of patrolling targets of the enemy.
--
-- ![Banner Image](..\Presentations\TASK_CARGO_DISPATCHER\Dia5.JPG)
--
-- Typically EWR networks are setup using 55G6 EWR, 1L13 EWR, Hawk sr and Patriot str ground based radar units.
-- These radars have different ranges and 55G6 EWR and 1L13 EWR radars are Eastern Bloc units (eg Russia, Ukraine, Georgia) while the Hawk and Patriot radars are Western (eg US).
-- Additionally, ANY other radar capable unit can be part of the EWR network! Also AWACS airborne units, planes, helicopters can help to detect targets, as long as they have radar.
-- The position of these units is very important as they need to provide enough coverage
-- to pick up enemy aircraft as they approach so that CAP and GCI flights can be tasked to intercept them.
--
-- ![Banner Image](..\Presentations\TASK_CARGO_DISPATCHER\Dia7.JPG)
--
-- Additionally in a hot war situation where the border is no longer respected the placement of radars has a big effect on how fast the war escalates.
-- For example if they are a long way forward and can detect enemy planes on the ground and taking off
-- they will start to vector CAP and GCI flights to attack them straight away which will immediately draw a response from the other coalition.
-- Having the radars further back will mean a slower escalation because fewer targets will be detected and
-- therefore less CAP and GCI flights will spawn and this will tend to make just the border area active rather than a melee over the whole map.
-- It all depends on what the desired effect is.
--
-- EWR networks are **dynamically constructed**, that is, they form part of the @{Functional#DETECTION_BASE} object that is given as the input parameter of the TASK\_A2A\_DISPATCHER class.
-- By defining in a **smart way the names or name prefixes of the groups** with EWR capable units, these groups will be **automatically added or deleted** from the EWR network,
-- increasing or decreasing the radar coverage of the Early Warning System.
--
-- See the following example to setup an EWR network containing EWR stations and AWACS.
--
-- local EWRSet = SET_GROUP:New():FilterPrefixes( "EWR" ):FilterCoalitions("red"):FilterStart()
--
-- local EWRDetection = DETECTION_AREAS:New( EWRSet, 6000 )
-- EWRDetection:SetFriendliesRange( 10000 )
-- EWRDetection:SetRefreshTimeInterval(30)
--
-- -- Setup the A2A dispatcher, and initialize it.
-- A2ADispatcher = TASK_CARGO_DISPATCHER:New( Mission, AttackGroups, EWRDetection )
--
-- The above example creates a SET_GROUP instance, and stores this in the variable (object) **EWRSet**.
-- **EWRSet** is then being configured to filter all active groups with a group name starting with **EWR** to be included in the Set.
-- **EWRSet** is then being ordered to start the dynamic filtering. Note that any destroy or new spawn of a group with the above names will be removed or added to the Set.
-- Then a new **EWRDetection** object is created from the class DETECTION_AREAS. A grouping radius of 6000 is choosen, which is 6km.
-- The **EWRDetection** object is then passed to the @{#TASK_CARGO_DISPATCHER.New}() method to indicate the EWR network configuration and setup the A2A tasking and detection mechanism.
--
-- ### 2. Define the detected **target grouping radius**:
--
-- ![Banner Image](..\Presentations\TASK_CARGO_DISPATCHER\Dia8.JPG)
--
-- The target grouping radius is a property of the Detection object, that was passed to the AI\_A2A\_DISPATCHER object, but can be changed.
-- The grouping radius should not be too small, but also depends on the types of planes and the era of the simulation.
-- Fast planes like in the 80s, need a larger radius than WWII planes.
-- Typically I suggest to use 30000 for new generation planes and 10000 for older era aircraft.
--
-- Note that detected targets are constantly re-grouped, that is, when certain detected aircraft are moving further than the group radius, then these aircraft will become a separate
-- group being detected. This may result in additional GCI being started by the dispatcher! So don't make this value too small!
--
-- ## 3. Set the **Engage radius**:
--
-- Define the radius to engage any target by airborne friendlies, which are executing cap or returning from an intercept mission.
--
-- ![Banner Image](..\Presentations\TASK_CARGO_DISPATCHER\Dia11.JPG)
--
-- So, if there is a target area detected and reported,
-- then any friendlies that are airborne near this target area,
-- will be commanded to (re-)engage that target when available (if no other tasks were commanded).
-- For example, if 100000 is given as a value, then any friendly that is airborne within 100km from the detected target,
-- will be considered to receive the command to engage that target area.
-- You need to evaluate the value of this parameter carefully.
-- If too small, more intercept missions may be triggered upon detected target areas.
-- If too large, any airborne cap may not be able to reach the detected target area in time, because it is too far.
--
-- ## 4. Set **Scoring** and **Messages**:
--
-- The TASK\_A2A\_DISPATCHER is a state machine. It triggers the event Assign when a new player joins a @{Task} dispatched by the TASK\_A2A\_DISPATCHER.
-- An _event handler_ can be defined to catch the **Assign** event, and add **additional processing** to set _scoring_ and to _define messages_,
-- when the player reaches certain achievements in the task.
--
-- The prototype to handle the **Assign** event needs to be developed as follows:
--
-- TaskDispatcher = TASK_CARGO_DISPATCHER:New( ... )
--
-- --- @param #TaskDispatcher self
-- -- @param #string From Contains the name of the state from where the Event was triggered.
-- -- @param #string Event Contains the name of the event that was triggered. In this case Assign.
-- -- @param #string To Contains the name of the state that will be transitioned to.
-- -- @param Tasking.Task_A2A#TASK_A2A Task The Task object, which is any derived object from TASK_A2A.
-- -- @param Wrapper.Unit#UNIT TaskUnit The Unit or Client that contains the Player.
-- -- @param #string PlayerName The name of the Player that joined the TaskUnit.
-- function TaskDispatcher:OnAfterAssign( From, Event, To, Task, TaskUnit, PlayerName )
-- Task:SetScoreOnProgress( PlayerName, 20, TaskUnit )
-- Task:SetScoreOnSuccess( PlayerName, 200, TaskUnit )
-- Task:SetScoreOnFail( PlayerName, -100, TaskUnit )
-- end
--
-- The **OnAfterAssign** method (function) is added to the TaskDispatcher object.
-- This method will be called when a new player joins a unit in the set of groups in scope of the dispatcher.
-- So, this method will be called only **ONCE** when a player joins a unit in scope of the task.
--
-- The TASK class implements various methods to additional **set scoring** for player achievements:
--
-- * @{Tasking.Task#TASK.SetScoreOnProgress}() will add additional scores when a player achieves **Progress** while executing the task.
-- Examples of **task progress** can be destroying units, arriving at zones etc.
--
-- * @{Tasking.Task#TASK.SetScoreOnSuccess}() will add additional scores when the task goes into **Success** state.
-- This means the **task has been successfully completed**.
--
-- * @{Tasking.Task#TASK.SetScoreOnSuccess}() will add additional (negative) scores when the task goes into **Failed** state.
-- This means the **task has not been successfully completed**, and the scores must be given with a negative value!
--
-- @field #TASK_CARGO_DISPATCHER
TASK_CARGO_DISPATCHER = {
ClassName = "TASK_CARGO_DISPATCHER",
Mission = nil,
Tasks = {},
CSAR = {},
CSARSpawned = 0,
Transport = {},
TransportCount = 0,
}
--- TASK_CARGO_DISPATCHER constructor.
-- @param #TASK_CARGO_DISPATCHER self
-- @param Tasking.Mission#MISSION Mission The mission for which the task dispatching is done.
-- @param Set#SET_GROUP SetGroup The set of groups that can join the tasks within the mission.
-- @return #TASK_CARGO_DISPATCHER self
function TASK_CARGO_DISPATCHER:New( Mission, SetGroup )
-- Inherits from DETECTION_MANAGER
local self = BASE:Inherit( self, TASK_MANAGER:New( SetGroup ) ) -- #TASK_CARGO_DISPATCHER
self.Mission = Mission
self:AddTransition( "Started", "Assign", "Started" )
--- OnAfter Transition Handler for Event Assign.
-- @function [parent=#TASK_CARGO_DISPATCHER] OnAfterAssign
-- @param #TASK_CARGO_DISPATCHER self
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @param Tasking.Task_A2A#TASK_A2A Task
-- @param Wrapper.Unit#UNIT TaskUnit
-- @param #string PlayerName
self:SetCSARRadius()
self:__StartTasks( 5 )
-- For CSAR missions, we process the event when a pilot ejects.
self:HandleEvent( EVENTS.Ejection )
return self
end
--- Handle the event when a pilot ejects.
-- @param #TASK_CARGO_DISPATCHER self
-- @param Core.Event#EVENTDATA EventData
function TASK_CARGO_DISPATCHER:OnEventEjection( EventData )
self:F( { EventData = EventData } )
if self.CSARTasks == true then
local CSARCoordinate = EventData.IniUnit:GetCoordinate()
local CSARCoalition = EventData.IniUnit:GetCoalition()
local CSARCountry = EventData.IniUnit:GetCountry()
local CSARHeading = EventData.IniUnit:GetHeading()
-- Only add a CSAR task if the coalition of the mission is equal to the coalition of the ejected unit.
if CSARCoalition == self.Mission:GetCommandCenter():GetCoalition() then
local CSARTaskName = self:AddCSARTask( self.CSARTaskName, CSARCoordinate, CSARHeading, CSARCountry, self.CSARBriefing )
self:SetCSARDeployZones( CSARTaskName, self.CSARDeployZones )
end
end
return self
end
--- Define one default deploy zone for all the cargo tasks.
-- @param #TASK_CARGO_DISPATCHER self
-- @param DefaultDeployZone A default deploy zone.
-- @return #TASK_CARGO_DISPATCHER
function TASK_CARGO_DISPATCHER:SetDefaultDeployZone( DefaultDeployZone )
self.DefaultDeployZones = { DefaultDeployZone }
return self
end
--- Define the deploy zones for all the cargo tasks.
-- @param #TASK_CARGO_DISPATCHER self
-- @param DefaultDeployZones A list of the deploy zones.
-- @return #TASK_CARGO_DISPATCHER
--
function TASK_CARGO_DISPATCHER:SetDefaultDeployZones( DefaultDeployZones )
self.DefaultDeployZones = DefaultDeployZones
return self
end
--- Start the generation of CSAR tasks to retrieve a downed pilots.
-- You need to specify a task briefing, a task name, default deployment zone(s).
-- This method can only be used once!
-- @param #TASK_CARGO_DISPATCHER self
-- @param #string CSARTaskName The CSAR task name.
-- @param #string CSARDeployZones The zones to where the CSAR deployment should be directed.
-- @param #string CSARBriefing The briefing of the CSAR tasks.
-- @return #TASK_CARGO_DISPATCHER
function TASK_CARGO_DISPATCHER:StartCSARTasks( CSARTaskName, CSARDeployZones, CSARBriefing)
if not self.CSARTasks then
self.CSARTasks = true
self.CSARTaskName = CSARTaskName
self.CSARDeployZones = CSARDeployZones
self.CSARBriefing = CSARBriefing
else
error( "TASK_CARGO_DISPATCHER: The generation of CSAR tasks has already started." )
end
return self
end
--- Stop the generation of CSAR tasks to retrieve a downed pilots.
-- @param #TASK_CARGO_DISPATCHER self
-- @return #TASK_CARGO_DISPATCHER
function TASK_CARGO_DISPATCHER:StopCSARTasks()
if self.CSARTasks then
self.CSARTasks = nil
self.CSARTaskName = nil
self.CSARDeployZones = nil
self.CSARBriefing = nil
else
error( "TASK_CARGO_DISPATCHER: The generation of CSAR tasks was not yet started." )
end
return self
end
--- Add a CSAR task to retrieve a downed pilot.
-- You need to specify a coordinate from where the pilot will be spawned to be rescued.
-- @param #TASK_CARGO_DISPATCHER self
-- @param #string CSARTaskPrefix (optional) The prefix of the CSAR task.
-- @param Core.Point#COORDINATE CSARCoordinate The coordinate where a downed pilot will be spawned.
-- @param #number CSARHeading The heading of the pilot in degrees.
-- @param DCSCountry#Country CSARCountry The country ID of the pilot that will be spawned.
-- @param #string CSARBriefing The briefing of the CSAR task.
-- @return #string The CSAR Task Name as a string. The Task Name is the main key and is shown in the task list of the Mission Tasking menu.
-- @usage
--
-- -- Add a CSAR task to rescue a downed pilot from within a coordinate.
-- local Coordinate = PlaneUnit:GetPointVec2()
-- TaskA2ADispatcher:AddCSARTask( Coordinate )
--
-- -- Add a CSAR task to rescue a downed pilot from within a coordinate of country RUSSIA, which is pointing to the west (270°).
-- local Coordinate = PlaneUnit:GetPointVec2()
-- TaskA2ADispatcher:AddCSARTask( Coordinate, 270, Country.RUSSIA )
--
function TASK_CARGO_DISPATCHER:AddCSARTask( CSARTaskPrefix, CSARCoordinate, CSARHeading, CSARCountry, CSARBriefing )
local CSARCoalition = self.Mission:GetCommandCenter():GetCoalition()
CSARHeading = CSARHeading or 0
CSARCountry = CSARCountry or self.Mission:GetCommandCenter():GetCountry()
self.CSARSpawned = self.CSARSpawned + 1
local CSARTaskName = string.format( ( CSARTaskPrefix or "CSAR" ) .. ".%03d", self.CSARSpawned )
-- Create the CSAR Pilot SPAWN object.
-- Let us create the Template for the replacement Pilot :-)
local Template = {
["visible"] = false,
["hidden"] = false,
["task"] = "Ground Nothing",
["name"] = string.format( "CSAR Pilot#%03d", self.CSARSpawned ),
["x"] = CSARCoordinate.x,
["y"] = CSARCoordinate.z,
["units"] =
{
[1] =
{
["type"] = ( CSARCoalition == coalition.side.BLUE ) and "Soldier M4" or "Infantry AK",
["name"] = string.format( "CSAR Pilot#%03d-01", self.CSARSpawned ),
["skill"] = "Excellent",
["playerCanDrive"] = false,
["x"] = CSARCoordinate.x,
["y"] = CSARCoordinate.z,
["heading"] = CSARHeading,
}, -- end of [1]
}, -- end of ["units"]
}
local CSARGroup = GROUP:NewTemplate( Template, CSARCoalition, Group.Category.GROUND, CSARCountry )
self.CSAR[CSARTaskName] = {}
self.CSAR[CSARTaskName].PilotGroup = CSARGroup
self.CSAR[CSARTaskName].Briefing = CSARBriefing
self.CSAR[CSARTaskName].Task = nil
return CSARTaskName
end
--- Define the radius to when a CSAR task will be generated for any downed pilot within range of the nearest CSAR airbase.
-- @param #TASK_CARGO_DISPATCHER self
-- @param #number CSARRadius (Optional, Default = 50000) The radius in meters to decide whether a CSAR needs to be created.
-- @return #TASK_CARGO_DISPATCHER
-- @usage
--
-- -- Set 20km as the radius to CSAR any downed pilot within range of the nearest CSAR airbase.
-- TaskA2ADispatcher:SetEngageRadius( 20000 )
--
-- -- Set 50km as the radius to to CSAR any downed pilot within range of the nearest CSAR airbase.
-- TaskA2ADispatcher:SetEngageRadius() -- 50000 is the default value.
--
function TASK_CARGO_DISPATCHER:SetCSARRadius( CSARRadius )
self.CSARRadius = CSARRadius or 50000
return self
end
--- Define one deploy zone for the CSAR tasks.
-- @param #TASK_CARGO_DISPATCHER self
-- @param #string CSARTaskName (optional) The name of the CSAR task.
-- @param CSARDeployZone A CSAR deploy zone.
-- @return #TASK_CARGO_DISPATCHER
function TASK_CARGO_DISPATCHER:SetCSARDeployZone( CSARTaskName, CSARDeployZone )
if CSARTaskName then
self.CSAR[CSARTaskName].DeployZones = { CSARDeployZone }
end
return self
end
--- Define the deploy zones for the CSAR tasks.
-- @param #TASK_CARGO_DISPATCHER self
-- @param #string CSARTaskName (optional) The name of the CSAR task.
-- @param CSARDeployZones A list of the CSAR deploy zones.
-- @return #TASK_CARGO_DISPATCHER
--
function TASK_CARGO_DISPATCHER:SetCSARDeployZones( CSARTaskName, CSARDeployZones )
if CSARTaskName and self.CSAR[CSARTaskName] then
self.CSAR[CSARTaskName].DeployZones = CSARDeployZones
end
return self
end
--- Add a Transport task to transport cargo from fixed locations to a deployment zone.
-- @param #TASK_CARGO_DISPATCHER self
-- @param #string TaskName (optional) The name of the transport task.
-- @param Core.SetCargo#SET_CARGO SetCargo The SetCargo to be transported.
-- @param #string Briefing The briefing of the task transport to be shown to the player.
-- @return #TASK_CARGO_DISPATCHER
-- @usage
--
-- -- Add a Transport task to transport cargo of different types to a Transport Deployment Zone.
function TASK_CARGO_DISPATCHER:AddTransportTask( TaskName, SetCargo, Briefing )
self.TransportCount = self.TransportCount + 1
local TaskName = string.format( ( TaskName or "Transport" ) .. ".%03d", self.TransportCount )
self.Transport[TaskName] = {}
self.Transport[TaskName].SetCargo = SetCargo
self.Transport[TaskName].Briefing = Briefing
self.Transport[TaskName].Task = nil
return TaskName
end
--- Add a Transport task to transport cargo from fixed locations to a deployment zone.
-- @param #TASK_CARGO_DISPATCHER self
-- @param #string TaskName (optional) The name of the transport task.
-- @return Tasking.Task_Cargo_Transport#TASK_CARGO_TRANSPORT
function TASK_CARGO_DISPATCHER:GetTransportTask( TaskName )
self:ManageTasks()
return self.Transport[TaskName] and self.Transport[TaskName].Task
end
--- Define one deploy zone for the Transport tasks.
-- @param #TASK_CARGO_DISPATCHER self
-- @param #string TaskName (optional) The name of the Transport task.
-- @param TransportDeployZone A Transport deploy zone.
-- @return #TASK_CARGO_DISPATCHER
function TASK_CARGO_DISPATCHER:SetTransportDeployZone( TaskName, TransportDeployZone )
if self.Transport[TaskName] then
self.Transport[TaskName].DeployZones = { TransportDeployZone }
else
error( "TaskName does not exist" )
end
return self
end
--- Define the deploy zones for the Transport tasks.
-- @param #TASK_CARGO_DISPATCHER self
-- @param #string TaskName (optional) The name of the Transport task.
-- @param TransportDeployZones A list of the Transport deploy zones.
-- @return #TASK_CARGO_DISPATCHER
--
function TASK_CARGO_DISPATCHER:SetTransportDeployZones( TaskName, TransportDeployZones )
if self.Transport[TaskName] then
self.Transport[TaskName].DeployZones = TransportDeployZones
else
error( "TaskName does not exist" )
end
return self
end
--- Evaluates of a CSAR task needs to be started.
-- @param #TASK_CARGO_DISPATCHER self
-- @return Set#SET_CARGO The SetCargo to be rescued.
-- @return #nil If there is no CSAR task required.
function TASK_CARGO_DISPATCHER:EvaluateCSAR( CSARUnit )
local CSARCargo = CARGO_GROUP:New( CSARUnit, "Pilot", CSARUnit:GetName(), 80, 1500, 10 )
local SetCargo = SET_CARGO:New()
SetCargo:AddCargosByName( CSARUnit:GetName() )
SetCargo:Flush(self)
return SetCargo
end
--- Assigns tasks to the @{Set#SET_GROUP}.
-- @param #TASK_CARGO_DISPATCHER self
-- @return #boolean Return true if you want the task assigning to continue... false will cancel the loop.
function TASK_CARGO_DISPATCHER:ManageTasks()
self:F()
local AreaMsg = {}
local TaskMsg = {}
local ChangeMsg = {}
local Mission = self.Mission
if Mission:IsIDLE() or Mission:IsENGAGED() then
local TaskReport = REPORT:New()
-- Checking the task queue for the dispatcher, and removing any obsolete task!
for TaskIndex, TaskData in pairs( self.Tasks ) do
local Task = TaskData -- Tasking.Task#TASK
if Task:IsStatePlanned() then
-- Here we need to check if the pilot is still existing.
-- local DetectedItem = Detection:GetDetectedItemByIndex( TaskIndex )
-- if not DetectedItem then
-- local TaskText = Task:GetName()
-- for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do
-- Mission:GetCommandCenter():MessageToGroup( string.format( "Obsolete A2A task %s for %s removed.", TaskText, Mission:GetShortText() ), TaskGroup )
-- end
-- Task = self:RemoveTask( TaskIndex )
-- end
end
end
-- Now that all obsolete tasks are removed, loop through the CSAR pilots.
for CSARName, CSAR in pairs( self.CSAR ) do
if not CSAR.Task then
-- New CSAR Task
local SetCargo = self:EvaluateCSAR( CSAR.PilotGroup )
CSAR.Task = TASK_CARGO_CSAR:New( Mission, self.SetGroup, CSARName, SetCargo, CSAR.Briefing )
Mission:AddTask( CSAR.Task )
TaskReport:Add( CSARName )
if CSAR.DeployZones then
CSAR.Task:SetDeployZones( CSAR.DeployZones or {} )
else
CSAR.Task:SetDeployZones( self.DefaultDeployZones or {} )
end
end
end
-- Now that all obsolete tasks are removed, loop through the Transport tasks.
for TransportName, Transport in pairs( self.Transport ) do
if not Transport.Task then
-- New Transport Task
Transport.Task = TASK_CARGO_TRANSPORT:New( Mission, self.SetGroup, TransportName, Transport.SetCargo, Transport.Briefing )
Mission:AddTask( Transport.Task )
TaskReport:Add( TransportName )
if Transport.DeployZones then
Transport.Task:SetDeployZones( Transport.DeployZones or {} )
else
Transport.Task:SetDeployZones( self.DefaultDeployZones or {} )
end
end
end
-- TODO set menus using the HQ coordinator
Mission:GetCommandCenter():SetMenu()
local TaskText = TaskReport:Text(", ")
for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do
if ( not Mission:IsGroupAssigned(TaskGroup) ) and TaskText ~= "" then
Mission:GetCommandCenter():MessageToGroup( string.format( "%s has tasks %s. Subscribe to a task using the radio menu.", Mission:GetShortText(), TaskText ), TaskGroup )
end
end
end
return true
end
end

View File

@@ -0,0 +1,112 @@
--- **Tasking** -- The TASK_CARGO models tasks for players to transport @{Cargo}.
--
-- ![Banner Image](..\Presentations\TASK_CARGO\Dia1.JPG)
--
-- ===
-- @module
do -- TASK_CARGO_TRANSPORT
--- The TASK_CARGO_TRANSPORT class
-- @type TASK_CARGO_TRANSPORT
-- @extends Tasking.Task_CARGO#TASK_CARGO
TASK_CARGO_TRANSPORT = {
ClassName = "TASK_CARGO_TRANSPORT",
}
--- Instantiates a new TASK_CARGO_TRANSPORT.
-- @param #TASK_CARGO_TRANSPORT self
-- @param Tasking.Mission#MISSION Mission
-- @param Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned.
-- @param #string TaskName The name of the Task.
-- @param Core.Set#SET_CARGO SetCargo The scope of the cargo to be transported.
-- @param #string TaskBriefing The Cargo Task briefing.
-- @return #TASK_CARGO_TRANSPORT self
function TASK_CARGO_TRANSPORT:New( Mission, SetGroup, TaskName, SetCargo, TaskBriefing )
local self = BASE:Inherit( self, TASK_CARGO:New( Mission, SetGroup, TaskName, SetCargo, "Transport", TaskBriefing ) ) -- #TASK_CARGO_TRANSPORT
self:F()
Mission:AddTask( self )
local Fsm = self:GetUnitProcess()
local CargoReport = REPORT:New( "Transport Cargo. The following cargo needs to be transported including initial positions:")
SetCargo:ForEachCargo(
--- @param Core.Cargo#CARGO Cargo
function( Cargo )
local CargoType = Cargo:GetType()
local CargoName = Cargo:GetName()
local CargoCoordinate = Cargo:GetCoordinate()
CargoReport:Add( string.format( '- "%s" (%s) at %s', CargoName, CargoType, CargoCoordinate:ToStringMGRS() ) )
end
)
self:SetBriefing(
TaskBriefing or
CargoReport:Text()
)
return self
end
function TASK_CARGO_TRANSPORT:ReportOrder( ReportGroup )
return 0
end
---
-- @param #TASK_CARGO_TRANSPORT self
-- @return #boolean
function TASK_CARGO_TRANSPORT:IsAllCargoTransported()
local CargoSet = self:GetCargoSet()
local Set = CargoSet:GetSet()
local DeployZones = self:GetDeployZones()
local CargoDeployed = true
-- Loop the CargoSet (so evaluate each Cargo in the SET_CARGO ).
for CargoID, CargoData in pairs( Set ) do
local Cargo = CargoData -- Core.Cargo#CARGO
self:F( { Cargo = Cargo:GetName(), CargoDeployed = Cargo:IsDeployed() } )
if Cargo:IsDeployed() then
-- -- Loop the DeployZones set for the TASK_CARGO_TRANSPORT.
-- for DeployZoneID, DeployZone in pairs( DeployZones ) do
--
-- -- If all cargo is in one of the deploy zones, then all is good.
-- self:T( { Cargo.CargoObject } )
-- if Cargo:IsInZone( DeployZone ) == false then
-- CargoDeployed = false
-- end
-- end
else
CargoDeployed = false
end
end
self:F( { CargoDeployed = CargoDeployed } )
return CargoDeployed
end
--- @param #TASK_CARGO_TRANSPORT self
function TASK_CARGO_TRANSPORT:onafterGoal( TaskUnit, From, Event, To )
local CargoSet = self.CargoSet
if self:IsAllCargoTransported() then
self:Success()
end
self:__Goal( -10 )
end
end

View File

@@ -0,0 +1,150 @@
--- This module contains the TASK_MANAGER class and derived classes.
--
-- ===
--
-- 1) @{Task_Manager#TASK_MANAGER} class, extends @{Fsm#FSM}
-- ===
-- The @{Task_Manager#TASK_MANAGER} class defines the core functions to report tasks to groups.
-- Reportings can be done in several manners, and it is up to the derived classes if TASK_MANAGER to model the reporting behaviour.
--
-- 1.1) TASK_MANAGER constructor:
-- -----------------------------------
-- * @{Task_Manager#TASK_MANAGER.New}(): Create a new TASK_MANAGER instance.
--
-- 1.2) TASK_MANAGER reporting:
-- ---------------------------------
-- Derived TASK_MANAGER classes will manage tasks using the method @{Task_Manager#TASK_MANAGER.ManageTasks}(). This method implements polymorphic behaviour.
--
-- The time interval in seconds of the task management can be changed using the methods @{Task_Manager#TASK_MANAGER.SetRefreshTimeInterval}().
-- To control how long a reporting message is displayed, use @{Task_Manager#TASK_MANAGER.SetReportDisplayTime}().
-- Derived classes need to implement the method @{Task_Manager#TASK_MANAGER.GetReportDisplayTime}() to use the correct display time for displayed messages during a report.
--
-- Task management can be started and stopped using the methods @{Task_Manager#TASK_MANAGER.StartTasks}() and @{Task_Manager#TASK_MANAGER.StopTasks}() respectively.
-- If an ad-hoc report is requested, use the method @{Task_Manager#TASK_MANAGER#ManageTasks}().
--
-- The default task management interval is every 60 seconds.
--
-- ===
--
-- ### Contributions: Mechanist, Prof_Hilactic, FlightControl - Concept & Testing
-- ### Author: FlightControl - Framework Design & Programming
--
-- @module Task_Manager
do -- TASK_MANAGER
--- TASK_MANAGER class.
-- @type TASK_MANAGER
-- @field Set#SET_GROUP SetGroup The set of group objects containing players for which tasks are managed.
-- @extends Core.Fsm#FSM
TASK_MANAGER = {
ClassName = "TASK_MANAGER",
SetGroup = nil,
}
--- TASK\_MANAGER constructor.
-- @param #TASK_MANAGER self
-- @param Set#SET_GROUP SetGroup The set of group objects containing players for which tasks are managed.
-- @return #TASK_MANAGER self
function TASK_MANAGER:New( SetGroup )
-- Inherits from BASE
local self = BASE:Inherit( self, FSM:New() ) -- #TASK_MANAGER
self.SetGroup = SetGroup
self:SetStartState( "Stopped" )
self:AddTransition( "Stopped", "StartTasks", "Started" )
--- StartTasks Handler OnBefore for TASK_MANAGER
-- @function [parent=#TASK_MANAGER] OnBeforeStartTasks
-- @param #TASK_MANAGER self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @return #boolean
--- StartTasks Handler OnAfter for TASK_MANAGER
-- @function [parent=#TASK_MANAGER] OnAfterStartTasks
-- @param #TASK_MANAGER self
-- @param #string From
-- @param #string Event
-- @param #string To
--- StartTasks Trigger for TASK_MANAGER
-- @function [parent=#TASK_MANAGER] StartTasks
-- @param #TASK_MANAGER self
--- StartTasks Asynchronous Trigger for TASK_MANAGER
-- @function [parent=#TASK_MANAGER] __StartTasks
-- @param #TASK_MANAGER self
-- @param #number Delay
self:AddTransition( "Started", "StopTasks", "Stopped" )
--- StopTasks Handler OnBefore for TASK_MANAGER
-- @function [parent=#TASK_MANAGER] OnBeforeStopTasks
-- @param #TASK_MANAGER self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @return #boolean
--- StopTasks Handler OnAfter for TASK_MANAGER
-- @function [parent=#TASK_MANAGER] OnAfterStopTasks
-- @param #TASK_MANAGER self
-- @param #string From
-- @param #string Event
-- @param #string To
--- StopTasks Trigger for TASK_MANAGER
-- @function [parent=#TASK_MANAGER] StopTasks
-- @param #TASK_MANAGER self
--- StopTasks Asynchronous Trigger for TASK_MANAGER
-- @function [parent=#TASK_MANAGER] __StopTasks
-- @param #TASK_MANAGER self
-- @param #number Delay
self:AddTransition( "Started", "Manage", "Started" )
self:SetRefreshTimeInterval( 30 )
return self
end
function TASK_MANAGER:onafterStartTasks( From, Event, To )
self:Manage()
end
function TASK_MANAGER:onafterManage( From, Event, To )
self:__Manage( -self._RefreshTimeInterval )
self:ManageTasks()
end
--- Set the refresh time interval in seconds when a new task management action needs to be done.
-- @param #TASK_MANAGER self
-- @param #number RefreshTimeInterval The refresh time interval in seconds when a new task management action needs to be done.
-- @return #TASK_MANAGER self
function TASK_MANAGER:SetRefreshTimeInterval( RefreshTimeInterval )
self:F2()
self._RefreshTimeInterval = RefreshTimeInterval
end
--- Manages the tasks for the @{Set#SET_GROUP}.
-- @param #TASK_MANAGER self
-- @return #TASK_MANAGER self
function TASK_MANAGER:ManageTasks()
self:E()
end
end

View File

@@ -29,6 +29,19 @@ SMOKECOLOR = trigger.smokeColor -- #SMOKECOLOR
FLARECOLOR = trigger.flareColor -- #FLARECOLOR FLARECOLOR = trigger.flareColor -- #FLARECOLOR
--- Big smoke preset enum.
-- @type BIGSMOKEPRESET
BIGSMOKEPRESET = {
SmallSmokeAndFire=0,
MediumSmokeAndFire=1,
LargeSmokeAndFire=2,
HugeSmokeAndFire=3,
SmallSmoke=4,
MediumSmoke=5,
LargeSmoke=6,
HugeSmoke=7,
}
--- Utilities static class. --- Utilities static class.
-- @type UTILS -- @type UTILS
UTILS = { UTILS = {

View File

@@ -179,11 +179,10 @@ function CLIENT:ShowBriefing()
if not self.ClientBriefingShown then if not self.ClientBriefingShown then
self.ClientBriefingShown = true self.ClientBriefingShown = true
local Briefing = "" local Briefing = ""
if self.ClientBriefing then if self.ClientBriefing and self.ClientBriefing ~= "" then
Briefing = Briefing .. self.ClientBriefing Briefing = Briefing .. self.ClientBriefing
self:Message( Briefing, 60, "Briefing" )
end end
Briefing = Briefing .. " Press [LEFT ALT]+[B] to view the complete mission briefing."
self:Message( Briefing, 60, "Briefing" )
end end
return self return self

View File

@@ -8,7 +8,7 @@
-- --
-- === -- ===
-- --
-- @module Controllable -- @module Wrapper.Controllable
@@ -202,26 +202,6 @@ end
-- Get methods -- Get methods
--- Returns the UNITs wrappers of the DCS Units of the Controllable (default is a GROUP).
-- @param #CONTROLLABLE self
-- @return #list<Wrapper.Unit#UNIT> The UNITs wrappers.
function CONTROLLABLE:GetUnits()
self:F2( { self.ControllableName } )
local DCSControllable = self:GetDCSObject()
if DCSControllable then
local DCSUnits = DCSControllable:getUnits()
local Units = {}
for Index, UnitData in pairs( DCSUnits ) do
Units[#Units+1] = UNIT:Find( UnitData )
end
self:T3( Units )
return Units
end
return nil
end
--- Returns the health. Dead controllables have health <= 1.0. --- Returns the health. Dead controllables have health <= 1.0.
-- @param #CONTROLLABLE self -- @param #CONTROLLABLE self
@@ -377,9 +357,11 @@ function CONTROLLABLE:SetTask( DCSTask, WaitTime )
local function SetTask( Controller, DCSTask ) local function SetTask( Controller, DCSTask )
if self and self:IsAlive() then if self and self:IsAlive() then
local Controller = self:_GetController() local Controller = self:_GetController()
self:I( "Before SetTask" )
Controller:setTask( DCSTask ) Controller:setTask( DCSTask )
self:I( "After SetTask" )
else else
BASE:E( DCSControllableName .. " is not alive anymore. Cannot set DCSTask " .. DCSTask ) BASE:E( { DCSControllableName .. " is not alive anymore.", DCSTask = DCSTask } )
end end
end end
@@ -516,7 +498,7 @@ function CONTROLLABLE:SetTaskWaypoint( Waypoint, Task )
Waypoint.task = self:TaskCombo( { Task } ) Waypoint.task = self:TaskCombo( { Task } )
self:T3( { Waypoint.task } ) self:F( { Waypoint.task } )
return Waypoint.task return Waypoint.task
end end
@@ -816,15 +798,16 @@ end
-- @param #CONTROLLABLE self -- @param #CONTROLLABLE self
-- @param #number Altitude The altitude to hold the position. -- @param #number Altitude The altitude to hold the position.
-- @param #number Speed The speed flying when holding the position. -- @param #number Speed The speed flying when holding the position.
-- @param Core.Point#COORDINATE Coordinate The coordinate where to orbit.
-- @return #CONTROLLABLE self -- @return #CONTROLLABLE self
function CONTROLLABLE:TaskOrbitCircle( Altitude, Speed ) function CONTROLLABLE:TaskOrbitCircle( Altitude, Speed, Coordinate )
self:F2( { self.ControllableName, Altitude, Speed } ) self:F2( { self.ControllableName, Altitude, Speed } )
local DCSControllable = self:GetDCSObject() local DCSControllable = self:GetDCSObject()
if DCSControllable then if DCSControllable then
local ControllablePoint = self:GetVec2() local OrbitVec2 = Coordinate and Coordinate:GetVec2() or self:GetVec2()
return self:TaskOrbitCircleAtVec2( ControllablePoint, Altitude, Speed ) return self:TaskOrbitCircleAtVec2( OrbitVec2, Altitude, Speed )
end end
return nil return nil
@@ -1068,9 +1051,10 @@ end
-- @param Dcs.DCSTypes#Vec2 Vec2 The point to fire at. -- @param Dcs.DCSTypes#Vec2 Vec2 The point to fire at.
-- @param Dcs.DCSTypes#Distance Radius The radius of the zone to deploy the fire at. -- @param Dcs.DCSTypes#Distance Radius The radius of the zone to deploy the fire at.
-- @param #number AmmoCount (optional) Quantity of ammunition to expand (omit to fire until ammunition is depleted). -- @param #number AmmoCount (optional) Quantity of ammunition to expand (omit to fire until ammunition is depleted).
-- @param #number WeaponType (optional) Enum for weapon type ID. This value is only required if you want the group firing to use a specific weapon, for instance using the task on a ship to force it to fire guided missiles at targets within cannon range. See http://wiki.hoggit.us/view/DCS_enum_weapon_flag
-- @return Dcs.DCSTasking.Task#Task The DCS task structure. -- @return Dcs.DCSTasking.Task#Task The DCS task structure.
function CONTROLLABLE:TaskFireAtPoint( Vec2, Radius, AmmoCount ) function CONTROLLABLE:TaskFireAtPoint( Vec2, Radius, AmmoCount, WeaponType )
self:F2( { self.ControllableName, Vec2, Radius, AmmoCount } ) self:F2( { self.ControllableName, Vec2, Radius, AmmoCount, WeaponType } )
-- FireAtPoint = { -- FireAtPoint = {
-- id = 'FireAtPoint', -- id = 'FireAtPoint',
@@ -1096,6 +1080,10 @@ function CONTROLLABLE:TaskFireAtPoint( Vec2, Radius, AmmoCount )
DCSTask.params.expendQty = AmmoCount DCSTask.params.expendQty = AmmoCount
DCSTask.params.expendQtyEnabled = true DCSTask.params.expendQtyEnabled = true
end end
if WeaponType then
DCSTask.params.weaponType=WeaponType
end
self:T3( { DCSTask } ) self:T3( { DCSTask } )
return DCSTask return DCSTask
@@ -1781,329 +1769,390 @@ function CONTROLLABLE:TaskRoute( Points )
return DCSTask return DCSTask
end end
--- (AIR + GROUND) Make the Controllable move to fly to a given point. do -- Route methods
-- @param #CONTROLLABLE self
-- @param Dcs.DCSTypes#Vec3 Point The destination point in Vec3 format.
-- @param #number Speed The speed to travel.
-- @return #CONTROLLABLE self
function CONTROLLABLE:RouteToVec2( Point, Speed )
self:F2( { Point, Speed } )
local ControllablePoint = self:GetUnit( 1 ):GetVec2() --- (AIR + GROUND) Make the Controllable move to fly to a given point.
-- @param #CONTROLLABLE self
local PointFrom = {} -- @param Dcs.DCSTypes#Vec3 Point The destination point in Vec3 format.
PointFrom.x = ControllablePoint.x -- @param #number Speed The speed to travel.
PointFrom.y = ControllablePoint.y -- @return #CONTROLLABLE self
PointFrom.type = "Turning Point" function CONTROLLABLE:RouteToVec2( Point, Speed )
PointFrom.action = "Turning Point" self:F2( { Point, Speed } )
PointFrom.speed = Speed
PointFrom.speed_locked = true
PointFrom.properties = {
["vnav"] = 1,
["scale"] = 0,
["angle"] = 0,
["vangle"] = 0,
["steer"] = 2,
}
local PointTo = {}
PointTo.x = Point.x
PointTo.y = Point.y
PointTo.type = "Turning Point"
PointTo.action = "Fly Over Point"
PointTo.speed = Speed
PointTo.speed_locked = true
PointTo.properties = {
["vnav"] = 1,
["scale"] = 0,
["angle"] = 0,
["vangle"] = 0,
["steer"] = 2,
}
local Points = { PointFrom, PointTo }
self:T3( Points )
self:Route( Points )
return self
end
--- (AIR + GROUND) Make the Controllable move to a given point.
-- @param #CONTROLLABLE self
-- @param Dcs.DCSTypes#Vec3 Point The destination point in Vec3 format.
-- @param #number Speed The speed to travel.
-- @return #CONTROLLABLE self
function CONTROLLABLE:RouteToVec3( Point, Speed )
self:F2( { Point, Speed } )
local ControllableVec3 = self:GetUnit( 1 ):GetVec3()
local PointFrom = {}
PointFrom.x = ControllableVec3.x
PointFrom.y = ControllableVec3.z
PointFrom.alt = ControllableVec3.y
PointFrom.alt_type = "BARO"
PointFrom.type = "Turning Point"
PointFrom.action = "Turning Point"
PointFrom.speed = Speed
PointFrom.speed_locked = true
PointFrom.properties = {
["vnav"] = 1,
["scale"] = 0,
["angle"] = 0,
["vangle"] = 0,
["steer"] = 2,
}
local PointTo = {}
PointTo.x = Point.x
PointTo.y = Point.z
PointTo.alt = Point.y
PointTo.alt_type = "BARO"
PointTo.type = "Turning Point"
PointTo.action = "Fly Over Point"
PointTo.speed = Speed
PointTo.speed_locked = true
PointTo.properties = {
["vnav"] = 1,
["scale"] = 0,
["angle"] = 0,
["vangle"] = 0,
["steer"] = 2,
}
local Points = { PointFrom, PointTo }
self:T3( Points )
self:Route( Points )
return self
end
--- Make the controllable to follow a given route.
-- @param #CONTROLLABLE self
-- @param #table Route A table of Route Points.
-- @param #number DelaySeconds Wait for the specified seconds before executing the Route.
-- @return #CONTROLLABLE The CONTROLLABLE.
function CONTROLLABLE:Route( Route, DelaySeconds )
self:F2( Route )
local DCSControllable = self:GetDCSObject()
if DCSControllable then
local RouteTask = self:TaskRoute( Route ) -- Create a RouteTask, that will route the CONTROLLABLE to the Route.
self:SetTask( RouteTask, DelaySeconds or 1 ) -- Execute the RouteTask after the specified seconds (default is 1).
return self
end
return nil
end
--- Make the GROUND Controllable to drive towards a specific point.
-- @param #CONTROLLABLE self
-- @param Core.Point#COORDINATE ToCoordinate A Coordinate to drive to.
-- @param #number Speed (optional) Speed in km/h. The default speed is 999 km/h.
-- @param #string Formation (optional) The route point Formation, which is a text string that specifies exactly the Text in the Type of the route point, like "Vee", "Echelon Right".
-- @param #number DelaySeconds Wait for the specified seconds before executing the Route.
-- @return #CONTROLLABLE The CONTROLLABLE.
function CONTROLLABLE:RouteGroundTo( ToCoordinate, Speed, Formation, DelaySeconds )
local FromCoordinate = self:GetCoordinate()
local FromWP = FromCoordinate:WaypointGround() local ControllablePoint = self:GetUnit( 1 ):GetVec2()
local ToWP = ToCoordinate:WaypointGround( Speed, Formation )
self:Route( { FromWP, ToWP }, DelaySeconds )
return self
end
--- Make the GROUND Controllable to drive towards a specific point using (only) roads.
-- @param #CONTROLLABLE self
-- @param Core.Point#COORDINATE ToCoordinate A Coordinate to drive to.
-- @param #number Speed (optional) Speed in km/h. The default speed is 999 km/h.
-- @param #number DelaySeconds Wait for the specified seconds before executing the Route.
-- @return #CONTROLLABLE The CONTROLLABLE.
function CONTROLLABLE:RouteGroundOnRoad( ToCoordinate, Speed, DelaySeconds )
-- Current coordinate.
local FromCoordinate = self:GetCoordinate()
-- Formation is set to on road.
local Formation="On Road"
-- Path on road from current position to destination coordinate.
local path=FromCoordinate:GetPathOnRoad(ToCoordinate)
-- Route, ground waypoints along roads.
local route={}
table.insert(route, FromCoordinate:WaypointGround(Speed, Formation))
-- Convert coordinates to ground waypoints and insert into table.
for _, coord in ipairs(path) do
table.insert(route, coord:WaypointGround(Speed, Formation))
end
-- Add the final coordinate because the final coordinate in path is last point on road.
local dist=ToCoordinate:Get2DDistance(path[#path])
if dist>10 then
table.insert(route, ToCoordinate:WaypointGround(Speed, "Vee"))
end
-- Route controllable to destination.
self:Route(route, DelaySeconds)
return self
end
--- Make the AIR Controllable fly towards a specific point.
-- @param #CONTROLLABLE self
-- @param Core.Point#COORDINATE ToCoordinate A Coordinate to drive to.
-- @param Core.Point#COORDINATE.RoutePointAltType AltType The altitude type.
-- @param Core.Point#COORDINATE.RoutePointType Type The route point type.
-- @param Core.Point#COORDINATE.RoutePointAction Action The route point action.
-- @param #number Speed (optional) Speed in km/h. The default speed is 999 km/h.
-- @param #number DelaySeconds Wait for the specified seconds before executing the Route.
-- @return #CONTROLLABLE The CONTROLLABLE.
function CONTROLLABLE:RouteAirTo( ToCoordinate, AltType, Type, Action, Speed, DelaySeconds )
local FromCoordinate = self:GetCoordinate()
local FromWP = FromCoordinate:WaypointAir()
local ToWP = ToCoordinate:WaypointAir( AltType, Type, Action, Speed )
self:Route( { FromWP, ToWP }, DelaySeconds )
return self
end
--- (AIR + GROUND) Route the controllable to a given zone.
-- The controllable final destination point can be randomized.
-- A speed can be given in km/h.
-- A given formation can be given.
-- @param #CONTROLLABLE self
-- @param Core.Zone#ZONE Zone The zone where to route to.
-- @param #boolean Randomize Defines whether to target point gets randomized within the Zone.
-- @param #number Speed The speed.
-- @param Base#FORMATION Formation The formation string.
function CONTROLLABLE:TaskRouteToZone( Zone, Randomize, Speed, Formation )
self:F2( Zone )
local DCSControllable = self:GetDCSObject()
if DCSControllable then
local ControllablePoint = self:GetVec2()
local PointFrom = {} local PointFrom = {}
PointFrom.x = ControllablePoint.x PointFrom.x = ControllablePoint.x
PointFrom.y = ControllablePoint.y PointFrom.y = ControllablePoint.y
PointFrom.type = "Turning Point" PointFrom.type = "Turning Point"
PointFrom.action = Formation or "Cone" PointFrom.action = "Turning Point"
PointFrom.speed = 20 / 1.6 PointFrom.speed = Speed
PointFrom.speed_locked = true
PointFrom.properties = {
["vnav"] = 1,
["scale"] = 0,
["angle"] = 0,
["vangle"] = 0,
["steer"] = 2,
}
local PointTo = {} local PointTo = {}
local ZonePoint PointTo.x = Point.x
PointTo.y = Point.y
if Randomize then
ZonePoint = Zone:GetRandomVec2()
else
ZonePoint = Zone:GetVec2()
end
PointTo.x = ZonePoint.x
PointTo.y = ZonePoint.y
PointTo.type = "Turning Point" PointTo.type = "Turning Point"
PointTo.action = "Fly Over Point"
if Formation then PointTo.speed = Speed
PointTo.action = Formation PointTo.speed_locked = true
else PointTo.properties = {
PointTo.action = "Cone" ["vnav"] = 1,
end ["scale"] = 0,
["angle"] = 0,
if Speed then ["vangle"] = 0,
PointTo.speed = Speed ["steer"] = 2,
else }
PointTo.speed = 20 / 1.6
end
local Points = { PointFrom, PointTo } local Points = { PointFrom, PointTo }
self:T3( Points ) self:T3( Points )
self:Route( Points ) self:Route( Points )
return self return self
end end
return nil --- (AIR + GROUND) Make the Controllable move to a given point.
end -- @param #CONTROLLABLE self
-- @param Dcs.DCSTypes#Vec3 Point The destination point in Vec3 format.
--- (GROUND) Route the controllable to a given Vec2. -- @param #number Speed The speed to travel.
-- A speed can be given in km/h. -- @return #CONTROLLABLE self
-- A given formation can be given. function CONTROLLABLE:RouteToVec3( Point, Speed )
-- @param #CONTROLLABLE self self:F2( { Point, Speed } )
-- @param #Vec2 Vec2 The Vec2 where to route to.
-- @param #number Speed The speed. local ControllableVec3 = self:GetUnit( 1 ):GetVec3()
-- @param Base#FORMATION Formation The formation string.
function CONTROLLABLE:TaskRouteToVec2( Vec2, Speed, Formation )
local DCSControllable = self:GetDCSObject()
if DCSControllable then
local ControllablePoint = self:GetVec2()
local PointFrom = {} local PointFrom = {}
PointFrom.x = ControllablePoint.x PointFrom.x = ControllableVec3.x
PointFrom.y = ControllablePoint.y PointFrom.y = ControllableVec3.z
PointFrom.alt = ControllableVec3.y
PointFrom.alt_type = "BARO"
PointFrom.type = "Turning Point" PointFrom.type = "Turning Point"
PointFrom.action = Formation or "Cone" PointFrom.action = "Turning Point"
PointFrom.speed = 20 / 1.6 PointFrom.speed = Speed
PointFrom.speed_locked = true
PointFrom.properties = {
["vnav"] = 1,
["scale"] = 0,
["angle"] = 0,
["vangle"] = 0,
["steer"] = 2,
}
local PointTo = {} local PointTo = {}
PointTo.x = Point.x
PointTo.x = Vec2.x PointTo.y = Point.z
PointTo.y = Vec2.y PointTo.alt = Point.y
PointTo.alt_type = "BARO"
PointTo.type = "Turning Point" PointTo.type = "Turning Point"
PointTo.action = "Fly Over Point"
if Formation then PointTo.speed = Speed
PointTo.action = Formation PointTo.speed_locked = true
else PointTo.properties = {
PointTo.action = "Cone" ["vnav"] = 1,
end ["scale"] = 0,
["angle"] = 0,
if Speed then ["vangle"] = 0,
PointTo.speed = Speed ["steer"] = 2,
else }
PointTo.speed = 60 / 3.6
end
local Points = { PointFrom, PointTo } local Points = { PointFrom, PointTo }
self:T3( Points ) self:T3( Points )
self:Route( Points ) self:Route( Points )
return self
end
--- Make the controllable to follow a given route.
-- @param #CONTROLLABLE self
-- @param #table Route A table of Route Points.
-- @param #number DelaySeconds Wait for the specified seconds before executing the Route.
-- @return #CONTROLLABLE The CONTROLLABLE.
function CONTROLLABLE:Route( Route, DelaySeconds )
self:F2( Route )
local DCSControllable = self:GetDCSObject()
if DCSControllable then
local RouteTask = self:TaskRoute( Route ) -- Create a RouteTask, that will route the CONTROLLABLE to the Route.
self:SetTask( RouteTask, DelaySeconds or 1 ) -- Execute the RouteTask after the specified seconds (default is 1).
return self
end
return nil
end
--- Stops the movement of the vehicle on the route.
-- @param #CONTROLLABLE self
-- @return #CONTROLLABLE
function CONTROLLABLE:RouteStop()
self:F("RouteStop")
local CommandStop = self:CommandStopRoute( true )
self:SetCommand( CommandStop )
end
--- Resumes the movement of the vehicle on the route.
-- @param #CONTROLLABLE self
-- @return #CONTROLLABLE
function CONTROLLABLE:RouteResume()
self:F("RouteResume")
local CommandResume = self:CommandStopRoute( false )
self:SetCommand( CommandResume )
end
--- Make the GROUND Controllable to drive towards a specific point.
-- @param #CONTROLLABLE self
-- @param Core.Point#COORDINATE ToCoordinate A Coordinate to drive to.
-- @param #number Speed (optional) Speed in km/h. The default speed is 999 km/h.
-- @param #string Formation (optional) The route point Formation, which is a text string that specifies exactly the Text in the Type of the route point, like "Vee", "Echelon Right".
-- @param #number DelaySeconds Wait for the specified seconds before executing the Route.
-- @return #CONTROLLABLE The CONTROLLABLE.
function CONTROLLABLE:RouteGroundTo( ToCoordinate, Speed, Formation, DelaySeconds )
local FromCoordinate = self:GetCoordinate()
local FromWP = FromCoordinate:WaypointGround()
local ToWP = ToCoordinate:WaypointGround( Speed, Formation )
self:Route( { FromWP, ToWP }, DelaySeconds )
return self
end
--- Make the GROUND Controllable to drive towards a specific point using (only) roads.
-- @param #CONTROLLABLE self
-- @param Core.Point#COORDINATE ToCoordinate A Coordinate to drive to.
-- @param #number Speed (optional) Speed in km/h. The default speed is 999 km/h.
-- @param #number DelaySeconds Wait for the specified seconds before executing the Route.
-- @return #CONTROLLABLE The CONTROLLABLE.
function CONTROLLABLE:RouteGroundOnRoad( ToCoordinate, Speed, DelaySeconds )
-- Current coordinate.
local FromCoordinate = self:GetCoordinate()
-- Formation is set to on road.
local Formation="On Road"
-- Path on road from current position to destination coordinate.
local path=FromCoordinate:GetPathOnRoad(ToCoordinate)
-- Route, ground waypoints along roads.
local route={}
table.insert(route, FromCoordinate:WaypointGround(Speed, Formation))
-- Convert coordinates to ground waypoints and insert into table.
for _, coord in ipairs(path) do
table.insert(route, coord:WaypointGround(Speed, Formation))
end
-- Add the final coordinate because the final coordinate in path is last point on road.
local dist=ToCoordinate:Get2DDistance(path[#path])
if dist>10 then
table.insert(route, ToCoordinate:WaypointGround(Speed, "Vee"))
end
-- Route controllable to destination.
self:Route( route, DelaySeconds )
return self return self
end end
return nil
end --- Make a task for a GROUND Controllable to drive towards a specific point using (only) roads.
-- @param #CONTROLLABLE self
-- @param Core.Point#COORDINATE ToCoordinate A Coordinate to drive to.
-- @param #number Speed (optional) Speed in km/h. The default speed is 999 km/h.
-- @param #string EndPointFormation The formation to achieve at the end point.
-- @return Task
function CONTROLLABLE:TaskGroundOnRoad( ToCoordinate, Speed, EndPointFormation )
-- Current coordinate.
local FromCoordinate = self:GetCoordinate()
-- Formation is set to on road.
local Formation="On Road"
-- Path on road from current position to destination coordinate.
local path=FromCoordinate:GetPathOnRoad( ToCoordinate )
-- Route, ground waypoints along roads.
local Route = {}
table.insert( Route, FromCoordinate:WaypointGround( Speed, Formation ) )
-- Convert coordinates to ground waypoints and insert into table.
for _, coord in ipairs(path) do
table.insert( Route, coord:WaypointGround( Speed, Formation ) )
end
-- Add the final coordinate because the final coordinate in path is last point on road.
local dist=ToCoordinate:Get2DDistance(path[#path])
if dist>10 then
table.insert( Route, ToCoordinate:WaypointGround( Speed, EndPointFormation ) )
end
return Route
end
--- Make the AIR Controllable fly towards a specific point.
-- @param #CONTROLLABLE self
-- @param Core.Point#COORDINATE ToCoordinate A Coordinate to drive to.
-- @param Core.Point#COORDINATE.RoutePointAltType AltType The altitude type.
-- @param Core.Point#COORDINATE.RoutePointType Type The route point type.
-- @param Core.Point#COORDINATE.RoutePointAction Action The route point action.
-- @param #number Speed (optional) Speed in km/h. The default speed is 999 km/h.
-- @param #number DelaySeconds Wait for the specified seconds before executing the Route.
-- @return #CONTROLLABLE The CONTROLLABLE.
function CONTROLLABLE:RouteAirTo( ToCoordinate, AltType, Type, Action, Speed, DelaySeconds )
local FromCoordinate = self:GetCoordinate()
local FromWP = FromCoordinate:WaypointAir()
local ToWP = ToCoordinate:WaypointAir( AltType, Type, Action, Speed )
self:Route( { FromWP, ToWP }, DelaySeconds )
return self
end
--- (AIR + GROUND) Route the controllable to a given zone.
-- The controllable final destination point can be randomized.
-- A speed can be given in km/h.
-- A given formation can be given.
-- @param #CONTROLLABLE self
-- @param Core.Zone#ZONE Zone The zone where to route to.
-- @param #boolean Randomize Defines whether to target point gets randomized within the Zone.
-- @param #number Speed The speed.
-- @param Base#FORMATION Formation The formation string.
function CONTROLLABLE:TaskRouteToZone( Zone, Randomize, Speed, Formation )
self:F2( Zone )
local DCSControllable = self:GetDCSObject()
if DCSControllable then
local ControllablePoint = self:GetVec2()
local PointFrom = {}
PointFrom.x = ControllablePoint.x
PointFrom.y = ControllablePoint.y
PointFrom.type = "Turning Point"
PointFrom.action = Formation or "Cone"
PointFrom.speed = 20 / 1.6
local PointTo = {}
local ZonePoint
if Randomize then
ZonePoint = Zone:GetRandomVec2()
else
ZonePoint = Zone:GetVec2()
end
PointTo.x = ZonePoint.x
PointTo.y = ZonePoint.y
PointTo.type = "Turning Point"
if Formation then
PointTo.action = Formation
else
PointTo.action = "Cone"
end
if Speed then
PointTo.speed = Speed
else
PointTo.speed = 20 / 1.6
end
local Points = { PointFrom, PointTo }
self:T3( Points )
self:Route( Points )
return self
end
return nil
end
--- (GROUND) Route the controllable to a given Vec2.
-- A speed can be given in km/h.
-- A given formation can be given.
-- @param #CONTROLLABLE self
-- @param #Vec2 Vec2 The Vec2 where to route to.
-- @param #number Speed The speed.
-- @param Base#FORMATION Formation The formation string.
function CONTROLLABLE:TaskRouteToVec2( Vec2, Speed, Formation )
local DCSControllable = self:GetDCSObject()
if DCSControllable then
local ControllablePoint = self:GetVec2()
local PointFrom = {}
PointFrom.x = ControllablePoint.x
PointFrom.y = ControllablePoint.y
PointFrom.type = "Turning Point"
PointFrom.action = Formation or "Cone"
PointFrom.speed = 20 / 1.6
local PointTo = {}
PointTo.x = Vec2.x
PointTo.y = Vec2.y
PointTo.type = "Turning Point"
if Formation then
PointTo.action = Formation
else
PointTo.action = "Cone"
end
if Speed then
PointTo.speed = Speed
else
PointTo.speed = 60 / 3.6
end
local Points = { PointFrom, PointTo }
self:T3( Points )
self:Route( Points )
return self
end
return nil
end
end -- Route methods
-- Commands -- Commands
@@ -2221,7 +2270,7 @@ function CONTROLLABLE:GetDetectedTargets( DetectVisual, DetectOptical, DetectRad
local DetectionRWR = ( DetectRWR and DetectRWR == true ) and Controller.Detection.RWR or nil local DetectionRWR = ( DetectRWR and DetectRWR == true ) and Controller.Detection.RWR or nil
local DetectionDLINK = ( DetectDLINK and DetectDLINK == true ) and Controller.Detection.DLINK or nil local DetectionDLINK = ( DetectDLINK and DetectDLINK == true ) and Controller.Detection.DLINK or nil
self:T( { DetectionVisual, DetectionOptical, DetectionRadar, DetectionIRST, DetectionRWR, DetectionDLINK } ) self:T2( { DetectionVisual, DetectionOptical, DetectionRadar, DetectionIRST, DetectionRWR, DetectionDLINK } )
return self:_GetController():getDetectedTargets( DetectionVisual, DetectionOptical, DetectionRadar, DetectionIRST, DetectionRWR, DetectionDLINK ) return self:_GetController():getDetectedTargets( DetectionVisual, DetectionOptical, DetectionRadar, DetectionIRST, DetectionRWR, DetectionDLINK )
end end
@@ -2781,16 +2830,6 @@ function CONTROLLABLE:IsAirPlane()
return nil return nil
end end
function CONTROLLABLE:GetSize()
local DCSObject = self:GetDCSObject()
if DCSObject then
return 1
else
return 0
end
end
-- Message APIs -- Message APIs

View File

@@ -23,7 +23,7 @@
-- --
-- === -- ===
-- --
-- @module Group -- @module Wrapper.Group
--- @type GROUP --- @type GROUP
@@ -121,7 +121,7 @@ GROUPTEMPLATE.Takeoff = {
-- @return #GROUP self -- @return #GROUP self
function GROUP:NewTemplate( GroupTemplate, CoalitionSide, CategoryID, CountryID ) function GROUP:NewTemplate( GroupTemplate, CoalitionSide, CategoryID, CountryID )
local GroupName = GroupTemplate.name local GroupName = GroupTemplate.name
_DATABASE:_RegisterGroupTemplate( GroupTemplate, CategoryID, CountryID, CoalitionSide, GroupName ) _DATABASE:_RegisterGroupTemplate( GroupTemplate, CoalitionSide, CategoryID, CountryID, GroupName )
self = BASE:Inherit( self, CONTROLLABLE:New( GroupName ) ) self = BASE:Inherit( self, CONTROLLABLE:New( GroupName ) )
self:F2( GroupName ) self:F2( GroupName )
self.GroupName = GroupName self.GroupName = GroupName
@@ -236,17 +236,37 @@ function GROUP:IsAlive()
end end
--- Destroys the DCS Group and all of its DCS Units. --- Destroys the DCS Group and all of its DCS Units.
-- Note that this destroy method also raises a destroy event at run-time. -- Note that this destroy method also can raise a destroy event at run-time.
-- So all event listeners will catch the destroy event of this DCS Group. -- So all event listeners will catch the destroy event of this group for each unit in the group.
-- To raise these events, provide the `GenerateEvent` parameter.
-- @param #GROUP self -- @param #GROUP self
function GROUP:Destroy() -- @param #boolean GenerateEvent true if you want to generate a crash or dead event for each unit.
-- @usage
-- -- Air unit example: destroy the Helicopter and generate a S_EVENT_CRASH for each unit in the Helicopter group.
-- Helicopter = GROUP:FindByName( "Helicopter" )
-- Helicopter:Destroy( true )
-- @usage
-- -- Ground unit example: destroy the Tanks and generate a S_EVENT_DEAD for each unit in the Tanks group.
-- Tanks = GROUP:FindByName( "Tanks" )
-- Tanks:Destroy( true )
-- @usage
-- -- Ship unit example: destroy the Ship silently.
-- Ship = GROUP:FindByName( "Ship" )
-- Ship:Destroy( true )
function GROUP:Destroy( GenerateEvent )
self:F2( self.GroupName ) self:F2( self.GroupName )
local DCSGroup = self:GetDCSObject() local DCSGroup = self:GetDCSObject()
if DCSGroup then if DCSGroup then
for Index, UnitData in pairs( DCSGroup:getUnits() ) do if GenerateEvent and GenerateEvent == true then
self:CreateEventCrash( timer.getTime(), UnitData ) for Index, UnitData in pairs( DCSGroup:getUnits() ) do
if self:IsAir() then
self:CreateEventCrash( timer.getTime(), UnitData )
else
self:CreateEventDead( timer.getTime(), UnitData )
end
end
end end
USERFLAG:New( self:GetName() ):Set( 100 ) USERFLAG:New( self:GetName() ):Set( 100 )
DCSGroup:destroy() DCSGroup:destroy()
@@ -330,13 +350,58 @@ function GROUP:GetCountry()
return nil return nil
end end
--- Returns a list of @{Unit} objects of the @{Group}.
-- @param #GROUP self
-- @return #list<Wrapper.Unit#UNIT> The list of @{Unit} objects of the @{Group}.
function GROUP:GetUnits()
self:F2( { self.GroupName } )
local DCSGroup = self:GetDCSObject()
if DCSGroup then
local DCSUnits = DCSGroup:getUnits()
local Units = {}
for Index, UnitData in pairs( DCSUnits ) do
Units[#Units+1] = UNIT:Find( UnitData )
end
self:T3( Units )
return Units
end
return nil
end
--- Returns a list of @{Unit} objects of the @{Group} that are occupied by a player.
-- @param #GROUP self
-- @return #list<Wrapper.Unit#UNIT> The list of player occupied @{Unit} objects of the @{Group}.
function GROUP:GetPlayerUnits()
self:F2( { self.GroupName } )
local DCSGroup = self:GetDCSObject()
if DCSGroup then
local DCSUnits = DCSGroup:getUnits()
local Units = {}
for Index, UnitData in pairs( DCSUnits ) do
local PlayerUnit = UNIT:Find( UnitData )
if PlayerUnit:GetPlayerName() then
Units[#Units+1] = PlayerUnit
end
end
self:T3( Units )
return Units
end
return nil
end
--- Returns the UNIT wrapper class with number UnitNumber. --- Returns the UNIT wrapper class with number UnitNumber.
-- If the underlying DCS Unit does not exist, the method will return nil. . -- If the underlying DCS Unit does not exist, the method will return nil. .
-- @param #GROUP self -- @param #GROUP self
-- @param #number UnitNumber The number of the UNIT wrapper class to be returned. -- @param #number UnitNumber The number of the UNIT wrapper class to be returned.
-- @return Wrapper.Unit#UNIT The UNIT wrapper class. -- @return Wrapper.Unit#UNIT The UNIT wrapper class.
function GROUP:GetUnit( UnitNumber ) function GROUP:GetUnit( UnitNumber )
self:F2( { self.GroupName, UnitNumber } ) self:F3( { self.GroupName, UnitNumber } )
local DCSGroup = self:GetDCSObject() local DCSGroup = self:GetDCSObject()
@@ -356,7 +421,7 @@ end
-- @param #number UnitNumber The number of the DCS Unit to be returned. -- @param #number UnitNumber The number of the DCS Unit to be returned.
-- @return Dcs.DCSWrapper.Unit#Unit The DCS Unit. -- @return Dcs.DCSWrapper.Unit#Unit The DCS Unit.
function GROUP:GetDCSUnit( UnitNumber ) function GROUP:GetDCSUnit( UnitNumber )
self:F2( { self.GroupName, UnitNumber } ) self:F3( { self.GroupName, UnitNumber } )
local DCSGroup = self:GetDCSObject() local DCSGroup = self:GetDCSObject()
@@ -374,7 +439,7 @@ end
-- @param #GROUP self -- @param #GROUP self
-- @return #number The DCS Group size. -- @return #number The DCS Group size.
function GROUP:GetSize() function GROUP:GetSize()
self:F2( { self.GroupName } ) self:F3( { self.GroupName } )
local DCSGroup = self:GetDCSObject() local DCSGroup = self:GetDCSObject()
if DCSGroup then if DCSGroup then
@@ -397,7 +462,7 @@ end
-- @param #GROUP self -- @param #GROUP self
-- @return #number The DCS Group initial size. -- @return #number The DCS Group initial size.
function GROUP:GetInitialSize() function GROUP:GetInitialSize()
self:F2( { self.GroupName } ) self:F3( { self.GroupName } )
local DCSGroup = self:GetDCSObject() local DCSGroup = self:GetDCSObject()
if DCSGroup then if DCSGroup then
@@ -1093,7 +1158,7 @@ function GROUP:Respawn( Template, Reset )
else else
for UnitID, TemplateUnitData in pairs( Template.units ) do for UnitID, TemplateUnitData in pairs( Template.units ) do
self:F( "Reset" ) self:F( "Reset" )
local GroupUnitVec3 = { x = TemplateUnitData.x, y = TemplateUnitData.alt, z = TemplateUnitData.z } local GroupUnitVec3 = { x = TemplateUnitData.x, y = TemplateUnitData.alt, z = TemplateUnitData.y }
if Zone then if Zone then
if self.InitRespawnRandomizePositionZone then if self.InitRespawnRandomizePositionZone then
GroupUnitVec3 = Zone:GetRandomVec3() GroupUnitVec3 = Zone:GetRandomVec3()
@@ -1121,10 +1186,106 @@ function GROUP:Respawn( Template, Reset )
self:ResetEvents() self:ResetEvents()
return self
end end
--- @param Wrapper.Group#GROUP self
function GROUP:RespawnAtAirbase( AirbaseRespawn, Takeoff, TakeoffAltitude ) -- R2.4
self:F( { AirbaseRespawn, Takeoff, TakeoffAltitude } )
local PointVec3 = AirbaseRespawn:GetPointVec3()
Takeoff = Takeoff or SPAWN.Takeoff.Hot
local SpawnTemplate = self:GetTemplate()
if SpawnTemplate then
local SpawnPoint = SpawnTemplate.route.points[1]
-- These are only for ships.
SpawnPoint.linkUnit = nil
SpawnPoint.helipadId = nil
SpawnPoint.airdromeId = nil
local AirbaseID = AirbaseRespawn:GetID()
local AirbaseCategory = AirbaseRespawn:GetDesc().category
self:F( { AirbaseCategory = AirbaseCategory, Ship = Airbase.Category.SHIP, Helipad = Airbase.Category.HELIPAD, Airdrome = Airbase.Category.AIRDROME } )
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
SpawnPoint.airdromeId = AirbaseID
end
SpawnPoint.alt = 0
SpawnPoint.type = GROUPTEMPLATE.Takeoff[Takeoff][1] -- type
SpawnPoint.action = GROUPTEMPLATE.Takeoff[Takeoff][2] -- action
-- Translate the position of the Group Template to the Vec3.
for UnitID = 1, #SpawnTemplate.units do
self:T( 'Before Translation SpawnTemplate.units['..UnitID..'].x = ' .. SpawnTemplate.units[UnitID].x .. ', SpawnTemplate.units['..UnitID..'].y = ' .. SpawnTemplate.units[UnitID].y )
-- These cause a lot of confusion.
local UnitTemplate = SpawnTemplate.units[UnitID]
UnitTemplate.parking = 15
UnitTemplate.parking_id = "30"
UnitTemplate.alt = 0
local SX = UnitTemplate.x
local SY = UnitTemplate.y
local BX = SpawnPoint.x
local BY = SpawnPoint.y
local TX = PointVec3.x + ( SX - BX )
local TY = PointVec3.z + ( SY - BY )
UnitTemplate.x = TX
UnitTemplate.y = TY
if Takeoff == GROUP.Takeoff.Air then
UnitTemplate.alt = PointVec3.y + ( TakeoffAltitude or 200 )
--else
-- UnitTemplate.alt = PointVec3.y + 10
end
self:T( 'After Translation SpawnTemplate.units['..UnitID..'].x = ' .. UnitTemplate.x .. ', SpawnTemplate.units['..UnitID..'].y = ' .. UnitTemplate.y )
end
SpawnPoint.x = PointVec3.x
SpawnPoint.y = PointVec3.z
if Takeoff == GROUP.Takeoff.Air then
SpawnPoint.alt = PointVec3.y + ( TakeoffAltitude or 200 )
--else
-- SpawnPoint.alt = PointVec3.y + 10
end
SpawnTemplate.x = PointVec3.x
SpawnTemplate.y = PointVec3.z
local GroupSpawned = self:Respawn( SpawnTemplate )
-- 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() } , 1 )
end
end
return GroupSpawned
end
return nil
end
--- Return the mission template of the group. --- Return the mission template of the group.
@@ -1250,7 +1411,7 @@ do -- Route methods
-- @param #number Speed (optional) The Speed, if no Speed is given, the maximum Speed of the first unit is selected. -- @param #number Speed (optional) The Speed, if no Speed is given, the maximum Speed of the first unit is selected.
-- @return #GROUP -- @return #GROUP
function GROUP:RouteRTB( RTBAirbase, Speed ) function GROUP:RouteRTB( RTBAirbase, Speed )
self:F2( { RTBAirbase, Speed } ) self:F( { RTBAirbase:GetName(), Speed } )
local DCSGroup = self:GetDCSObject() local DCSGroup = self:GetDCSObject()
@@ -1291,9 +1452,7 @@ do -- Route methods
Template.route.points = Points Template.route.points = Points
self:Respawn( Template ) self:Respawn( Template )
self:Route( Points ) --self:Route( Points )
self:Respawn(Template)
else else
self:ClearTasks() self:ClearTasks()
end end
@@ -1358,6 +1517,8 @@ do -- Players
-- @return #nil The group has no players -- @return #nil The group has no players
function GROUP:GetPlayerNames() function GROUP:GetPlayerNames()
local HasPlayers = false
local PlayerNames = {} local PlayerNames = {}
local Units = self:GetUnits() local Units = self:GetUnits()
@@ -1367,11 +1528,36 @@ do -- Players
if PlayerName and PlayerName ~= "" then if PlayerName and PlayerName ~= "" then
PlayerNames = PlayerNames or {} PlayerNames = PlayerNames or {}
table.insert( PlayerNames, PlayerName ) table.insert( PlayerNames, PlayerName )
HasPlayers = true
end end
end end
if HasPlayers == true then
self:F2( PlayerNames )
return PlayerNames
end
self:F2( PlayerNames ) return nil
return PlayerNames end
--- Get the active player count in the group.
-- @param #GROUP self
-- @return #number The amount of players.
function GROUP:GetPlayerCount()
local PlayerCount = 0
local Units = self:GetUnits()
for UnitID, UnitData in pairs( Units or {} ) do
local Unit = UnitData -- Wrapper.Unit#UNIT
local PlayerName = Unit:GetPlayerName()
if PlayerName and PlayerName ~= "" then
PlayerCount = PlayerCount + 1
end
end
return PlayerCount
end end
end end

View File

@@ -229,6 +229,26 @@ function IDENTIFIABLE:GetDesc()
return nil return nil
end end
--- Check if the Object has the attribute.
-- @param #IDENTIFIABLE self
-- @param #string AttributeName The attribute name.
-- @return #boolean true if the attribute exists.
-- @return #nil The DCS Identifiable is not existing or alive.
function IDENTIFIABLE:HasAttribute( AttributeName )
self:F2( self.IdentifiableName )
local DCSIdentifiable = self:GetDCSObject()
if DCSIdentifiable then
local IdentifiableHasAttribute = DCSIdentifiable:hasAttribute( AttributeName )
self:T2( IdentifiableHasAttribute )
return IdentifiableHasAttribute
end
self:F( self.ClassName .. " " .. self.IdentifiableName .. " not found!" )
return nil
end
--- Gets the CallSign of the IDENTIFIABLE, which is a blank by default. --- Gets the CallSign of the IDENTIFIABLE, which is a blank by default.
-- @param #IDENTIFIABLE self -- @param #IDENTIFIABLE self
-- @return #string The CallSign of the IDENTIFIABLE. -- @return #string The CallSign of the IDENTIFIABLE.

View File

@@ -73,6 +73,7 @@ end
--- Destroys the OBJECT. --- Destroys the OBJECT.
-- @param #OBJECT self -- @param #OBJECT self
-- @return #boolean true if the object is destroyed.
-- @return #nil The DCS Unit is not existing or alive. -- @return #nil The DCS Unit is not existing or alive.
function OBJECT:Destroy() function OBJECT:Destroy()
@@ -80,7 +81,8 @@ function OBJECT:Destroy()
if DCSObject then if DCSObject then
--BASE:CreateEventCrash( timer.getTime(), DCSObject ) --BASE:CreateEventCrash( timer.getTime(), DCSObject )
DCSObject:destroy() DCSObject:destroy( false )
return true
end end
BASE:E( { "Cannot Destroy", Name = self.ObjectName, Class = self:GetClassName() } ) BASE:E( { "Cannot Destroy", Name = self.ObjectName, Class = self:GetClassName() } )
@@ -91,3 +93,5 @@ end

View File

@@ -129,7 +129,7 @@ function POSITIONABLE:GetPointVec2()
local PositionablePointVec2 = POINT_VEC2:NewFromVec3( PositionableVec3 ) local PositionablePointVec2 = POINT_VEC2:NewFromVec3( PositionableVec3 )
self:T2( PositionablePointVec2 ) --self:F( PositionablePointVec2 )
return PositionablePointVec2 return PositionablePointVec2
end end
@@ -309,6 +309,18 @@ function POSITIONABLE:IsAboveRunway()
end end
function POSITIONABLE:GetSize()
local DCSObject = self:GetDCSObject()
if DCSObject then
return 1
else
return 0
end
end
--- Returns the POSITIONABLE heading in degrees. --- Returns the POSITIONABLE heading in degrees.
-- @param Wrapper.Positionable#POSITIONABLE self -- @param Wrapper.Positionable#POSITIONABLE self
@@ -379,7 +391,7 @@ function POSITIONABLE:GetVelocityVec3()
local DCSPositionable = self:GetDCSObject() local DCSPositionable = self:GetDCSObject()
if DCSPositionable then if DCSPositionable and DCSPositionable:isExist() then
local PositionableVelocityVec3 = DCSPositionable:getVelocity() local PositionableVelocityVec3 = DCSPositionable:getVelocity()
self:T3( PositionableVelocityVec3 ) self:T3( PositionableVelocityVec3 )
return PositionableVelocityVec3 return PositionableVelocityVec3
@@ -421,7 +433,7 @@ function POSITIONABLE:GetVelocityKMH()
local DCSPositionable = self:GetDCSObject() local DCSPositionable = self:GetDCSObject()
if DCSPositionable then if DCSPositionable and DCSPositionable:isExist() then
local VelocityVec3 = self:GetVelocityVec3() local VelocityVec3 = self:GetVelocityVec3()
local Velocity = ( VelocityVec3.x ^ 2 + VelocityVec3.y ^ 2 + VelocityVec3.z ^ 2 ) ^ 0.5 -- in meters / sec local Velocity = ( VelocityVec3.x ^ 2 + VelocityVec3.y ^ 2 + VelocityVec3.z ^ 2 ) ^ 0.5 -- in meters / sec
local Velocity = Velocity * 3.6 -- now it is in km/h. local Velocity = Velocity * 3.6 -- now it is in km/h.
@@ -440,7 +452,7 @@ function POSITIONABLE:GetVelocityMPS()
local DCSPositionable = self:GetDCSObject() local DCSPositionable = self:GetDCSObject()
if DCSPositionable then if DCSPositionable and DCSPositionable:isExist() then
local VelocityVec3 = self:GetVelocityVec3() local VelocityVec3 = self:GetVelocityVec3()
local Velocity = ( VelocityVec3.x ^ 2 + VelocityVec3.y ^ 2 + VelocityVec3.z ^ 2 ) ^ 0.5 -- in meters / sec local Velocity = ( VelocityVec3.x ^ 2 + VelocityVec3.y ^ 2 + VelocityVec3.z ^ 2 ) ^ 0.5 -- in meters / sec
self:T3( Velocity ) self:T3( Velocity )
@@ -526,10 +538,11 @@ end
-- @param #string Message The message text -- @param #string Message The message text
-- @param Dcs.DCSTYpes#Duration Duration The duration of the message. -- @param Dcs.DCSTYpes#Duration Duration The duration of the message.
-- @param Dcs.DCScoalition#coalition MessageCoalition The Coalition receiving the message. -- @param Dcs.DCScoalition#coalition MessageCoalition The Coalition receiving the message.
function POSITIONABLE:MessageToCoalition( Message, Duration, MessageCoalition ) -- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable.
function POSITIONABLE:MessageToCoalition( Message, Duration, MessageCoalition, Name )
self:F2( { Message, Duration } ) self:F2( { Message, Duration } )
local Name = "" local Name = Name or ""
local DCSObject = self:GetDCSObject() local DCSObject = self:GetDCSObject()
if DCSObject then if DCSObject then
@@ -546,10 +559,11 @@ end
-- @param #string Message The message text -- @param #string Message The message text
-- @param Core.Message#MESSAGE.Type MessageType The message type that determines the duration. -- @param Core.Message#MESSAGE.Type MessageType The message type that determines the duration.
-- @param Dcs.DCScoalition#coalition MessageCoalition The Coalition receiving the message. -- @param Dcs.DCScoalition#coalition MessageCoalition The Coalition receiving the message.
function POSITIONABLE:MessageTypeToCoalition( Message, MessageType, MessageCoalition ) -- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable.
function POSITIONABLE:MessageTypeToCoalition( Message, MessageType, MessageCoalition, Name )
self:F2( { Message, MessageType } ) self:F2( { Message, MessageType } )
local Name = "" local Name = Name or ""
local DCSObject = self:GetDCSObject() local DCSObject = self:GetDCSObject()
if DCSObject then if DCSObject then
@@ -794,6 +808,15 @@ function POSITIONABLE:AddCargo( Cargo )
return self return self
end end
--- Get all contained cargo.
-- @param #POSITIONABLE self
-- @return #POSITIONABLE
function POSITIONABLE:GetCargo()
return self.__.Cargo
end
--- Remove cargo. --- Remove cargo.
-- @param #POSITIONABLE self -- @param #POSITIONABLE self
-- @param Core.Cargo#CARGO Cargo -- @param Core.Cargo#CARGO Cargo

View File

@@ -48,6 +48,24 @@ STATIC = {
} }
function STATIC:Register( StaticName )
local self = BASE:Inherit( self, POSITIONABLE:New( StaticName ) )
self.StaticName = StaticName
return self
end
--- Finds a STATIC from the _DATABASE using a DCSStatic object.
-- @param #STATIC self
-- @param Dcs.DCSWrapper.Static#Static DCSStatic An existing DCS Static object reference.
-- @return #STATIC self
function STATIC:Find( DCSStatic )
local StaticName = DCSStatic:getName()
local StaticFound = _DATABASE:FindStatic( StaticName )
return StaticFound
end
--- Finds a STATIC from the _DATABASE using the relevant Static Name. --- Finds a STATIC from the _DATABASE using the relevant Static Name.
-- As an optional parameter, a briefing text can be given also. -- As an optional parameter, a briefing text can be given also.
-- @param #STATIC self -- @param #STATIC self
@@ -71,12 +89,6 @@ function STATIC:FindByName( StaticName, RaiseError )
return nil return nil
end end
function STATIC:Register( StaticName )
local self = BASE:Inherit( self, POSITIONABLE:New( StaticName ) )
self.StaticName = StaticName
return self
end
function STATIC:GetDCSObject() function STATIC:GetDCSObject()
local DCSStatic = StaticObject.getByName( self.StaticName ) local DCSStatic = StaticObject.getByName( self.StaticName )
@@ -88,6 +100,27 @@ function STATIC:GetDCSObject()
return nil return nil
end end
--- Returns a list of one @{Static}.
-- @param #STATIC self
-- @return #list<Wrapper.Static#STATIC> A list of one @{Static}.
function STATIC:GetUnits()
self:F2( { self.StaticName } )
local DCSStatic = self:GetDCSObject()
local Statics = {}
if DCSStatic then
Statics[1] = STATIC:Find( DCSStatic )
self:T3( Statics )
return Statics
end
return nil
end
function STATIC:GetThreatLevel() function STATIC:GetThreatLevel()
return 1, "Static" return 1, "Static"
@@ -97,12 +130,32 @@ end
-- @param #UNIT self -- @param #UNIT self
-- @param Core.Point#COORDINATE Coordinate The coordinate where to spawn the new Static. -- @param Core.Point#COORDINATE Coordinate The coordinate where to spawn the new Static.
-- @param #number Heading The heading of the unit respawn. -- @param #number Heading The heading of the unit respawn.
function STATIC:ReSpawn( Coordinate, Heading ) function STATIC:SpawnAt( Coordinate, Heading )
local SpawnStatic = SPAWNSTATIC:NewFromStatic( self.StaticName )
-- todo: need to fix country
local SpawnStatic = SPAWNSTATIC:NewFromStatic( self.StaticName, country.id.USA )
SpawnStatic:SpawnFromPointVec2( Coordinate, Heading, self.StaticName ) SpawnStatic:SpawnFromPointVec2( Coordinate, Heading, self.StaticName )
end end
--- Respawn the @{Unit} at the same location with the same properties.
-- This is useful to respawn a cargo after it has been destroyed.
-- @param #UNIT self
function STATIC:ReSpawn()
local SpawnStatic = SPAWNSTATIC:NewFromStatic( self.StaticName )
SpawnStatic:ReSpawn()
end
--- Respawn the @{Unit} at a defined Coordinate with an optional heading.
-- @param #UNIT self
-- @param Core.Point#COORDINATE Coordinate The coordinate where to spawn the new Static.
-- @param #number Heading The heading of the unit respawn.
function STATIC:ReSpawnAt( Coordinate, Heading )
local SpawnStatic = SPAWNSTATIC:NewFromStatic( self.StaticName )
SpawnStatic:ReSpawnAt( Coordinate, Heading )
end

View File

@@ -161,18 +161,28 @@ end
--- Destroys the UNIT. --- Destroys the UNIT.
-- @param #UNIT self -- @param #UNIT self
-- @param #boolean GenerateEvent (Optional) true if you want to generate a crash or dead event for the unit.
-- @return #nil The DCS Unit is not existing or alive. -- @return #nil The DCS Unit is not existing or alive.
function UNIT:Destroy() function UNIT:Destroy( GenerateEvent )
self:F2( self.ObjectName ) self:F2( self.ObjectName )
local DCSObject = self:GetDCSObject() local DCSObject = self:GetDCSObject()
if DCSObject then if DCSObject then
local UnitGroup = self:GetGroup() local UnitGroup = self:GetGroup()
local UnitGroupName = UnitGroup:GetName() local UnitGroupName = UnitGroup:GetName()
self:F( { UnitGroupName = UnitGroupName } ) self:F( { UnitGroupName = UnitGroupName } )
if GenerateEvent and GenerateEvent == true then
if self:IsAir() then
self:CreateEventCrash( timer.getTime(), DCSObject )
else
self:CreateEventDead( timer.getTime(), DCSObject )
end
end
USERFLAG:New( UnitGroupName ):Set( 100 ) USERFLAG:New( UnitGroupName ):Set( 100 )
--BASE:CreateEventCrash( timer.getTime(), DCSObject )
DCSObject:destroy() DCSObject:destroy()
end end
@@ -189,20 +199,22 @@ end
-- * Then it will respawn the re-modelled group. -- * Then it will respawn the re-modelled group.
-- --
-- @param #UNIT self -- @param #UNIT self
-- @param Dcs.DCSTypes#Vec3 SpawnVec3 The position where to Spawn the new Unit at. -- @param Core.Point#COORDINATE Coordinate The position where to Spawn the new Unit at.
-- @param #number Heading The heading of the unit respawn. -- @param #number Heading The heading of the unit respawn.
function UNIT:ReSpawn( SpawnVec3, Heading ) function UNIT:ReSpawnAt( Coordinate, Heading )
self:T( self:Name() )
local SpawnGroupTemplate = UTILS.DeepCopy( _DATABASE:GetGroupTemplateFromUnitName( self:Name() ) ) local SpawnGroupTemplate = UTILS.DeepCopy( _DATABASE:GetGroupTemplateFromUnitName( self:Name() ) )
self:T( SpawnGroupTemplate ) self:T( SpawnGroupTemplate )
local SpawnGroup = self:GetGroup() local SpawnGroup = self:GetGroup()
self:T( { SpawnGroup = SpawnGroup } )
if SpawnGroup then if SpawnGroup then
local Vec3 = SpawnGroup:GetVec3() local Vec3 = SpawnGroup:GetVec3()
SpawnGroupTemplate.x = SpawnVec3.x SpawnGroupTemplate.x = Coordinate.x
SpawnGroupTemplate.y = SpawnVec3.z SpawnGroupTemplate.y = Coordinate.z
self:F( #SpawnGroupTemplate.units ) self:F( #SpawnGroupTemplate.units )
for UnitID, UnitData in pairs( SpawnGroup:GetUnits() ) do for UnitID, UnitData in pairs( SpawnGroup:GetUnits() ) do
@@ -221,12 +233,13 @@ function UNIT:ReSpawn( SpawnVec3, Heading )
end end
for UnitTemplateID, UnitTemplateData in pairs( SpawnGroupTemplate.units ) do for UnitTemplateID, UnitTemplateData in pairs( SpawnGroupTemplate.units ) do
self:T( UnitTemplateData.name ) self:T( { UnitTemplateData.name, self:Name() } )
SpawnGroupTemplate.units[UnitTemplateID].unitId = nil
if UnitTemplateData.name == self:Name() then if UnitTemplateData.name == self:Name() then
self:T("Adjusting") self:T("Adjusting")
SpawnGroupTemplate.units[UnitTemplateID].alt = SpawnVec3.y SpawnGroupTemplate.units[UnitTemplateID].alt = Coordinate.y
SpawnGroupTemplate.units[UnitTemplateID].x = SpawnVec3.x SpawnGroupTemplate.units[UnitTemplateID].x = Coordinate.x
SpawnGroupTemplate.units[UnitTemplateID].y = SpawnVec3.z SpawnGroupTemplate.units[UnitTemplateID].y = Coordinate.z
SpawnGroupTemplate.units[UnitTemplateID].heading = Heading SpawnGroupTemplate.units[UnitTemplateID].heading = Heading
self:F( { UnitTemplateID, SpawnGroupTemplate.units[UnitTemplateID], SpawnGroupTemplate.units[UnitTemplateID] } ) self:F( { UnitTemplateID, SpawnGroupTemplate.units[UnitTemplateID], SpawnGroupTemplate.units[UnitTemplateID] } )
else else
@@ -261,6 +274,10 @@ function UNIT:ReSpawn( SpawnVec3, Heading )
i = i + 1 i = i + 1
end end
end end
SpawnGroupTemplate.groupId = nil
self:T( SpawnGroupTemplate )
_DATABASE:Spawn( SpawnGroupTemplate ) _DATABASE:Spawn( SpawnGroupTemplate )
end end
@@ -319,6 +336,9 @@ function UNIT:GetCallsign()
if DCSUnit then if DCSUnit then
local UnitCallSign = DCSUnit:getCallsign() local UnitCallSign = DCSUnit:getCallsign()
if UnitCallSign == "" then
UnitCallSign = DCSUnit:getName()
end
return UnitCallSign return UnitCallSign
end end
@@ -334,18 +354,30 @@ end
function UNIT:GetPlayerName() function UNIT:GetPlayerName()
self:F2( self.UnitName ) self:F2( self.UnitName )
local DCSUnit = self:GetDCSObject() local DCSUnit = self:GetDCSObject() -- Dcs.DCSUnit#Unit
if DCSUnit then if DCSUnit then
local PlayerName = DCSUnit:getPlayerName() local PlayerName = DCSUnit:getPlayerName()
if PlayerName == nil then -- TODO Workaround DCS-BUG-3 - https://github.com/FlightControl-Master/MOOSE/issues/696
PlayerName = "" if PlayerName == nil or PlayerName == "" then
local PlayerCategory = DCSUnit:getDesc().category
if PlayerCategory == Unit.Category.GROUND_UNIT or PlayerCategory == Unit.Category.SHIP then
PlayerName = "Player" .. DCSUnit:getID()
end
end end
-- -- Good code
-- if PlayerName == nil then
-- PlayerName = nil
-- else
-- if PlayerName == "" then
-- PlayerName = "Player" .. DCSUnit:getID()
-- end
-- end
return PlayerName return PlayerName
end end
return nil return nil
end end
@@ -379,7 +411,7 @@ function UNIT:GetGroup()
local DCSUnit = self:GetDCSObject() local DCSUnit = self:GetDCSObject()
if DCSUnit then if DCSUnit then
local UnitGroup = GROUP:Find( DCSUnit:getGroup() ) local UnitGroup = GROUP:FindByName( DCSUnit:getGroup():getName() )
return UnitGroup return UnitGroup
end end
@@ -524,16 +556,16 @@ function UNIT:GetFuel()
return nil return nil
end end
--- Returns the UNIT in a UNIT list of one element. --- Returns a list of one @{Unit}.
-- @param #UNIT self -- @param #UNIT self
-- @return #list<Wrapper.Unit#UNIT> The UNITs wrappers. -- @return #list<Wrapper.Unit#UNIT> A list of one @{Unit}.
function UNIT:GetUnits() function UNIT:GetUnits()
self:F2( { self.UnitName } ) self:F2( { self.UnitName } )
local DCSUnit = self:GetDCSObject() local DCSUnit = self:GetDCSObject()
local Units = {}
if DCSUnit then if DCSUnit then
local DCSUnits = DCSUnit:getUnits()
local Units = {}
Units[1] = UNIT:Find( DCSUnit ) Units[1] = UNIT:Find( DCSUnit )
self:T3( Units ) self:T3( Units )
return Units return Units
@@ -628,12 +660,9 @@ function UNIT:GetThreatLevel()
if Descriptor then if Descriptor then
local Attributes = Descriptor.attributes local Attributes = Descriptor.attributes
self:T( Attributes )
if self:IsGround() then if self:IsGround() then
self:T( "Ground" )
local ThreatLevels = { local ThreatLevels = {
"Unarmed", "Unarmed",
"Infantry", "Infantry",
@@ -670,8 +699,6 @@ function UNIT:GetThreatLevel()
if self:IsAir() then if self:IsAir() then
self:T( "Air" )
local ThreatLevels = { local ThreatLevels = {
"Unarmed", "Unarmed",
"Tanker", "Tanker",
@@ -704,8 +731,6 @@ function UNIT:GetThreatLevel()
if self:IsShip() then if self:IsShip() then
self:T( "Ship" )
--["Aircraft Carriers"] = {"Heavy armed ships",}, --["Aircraft Carriers"] = {"Heavy armed ships",},
--["Cruisers"] = {"Heavy armed ships",}, --["Cruisers"] = {"Heavy armed ships",},
--["Destroyers"] = {"Heavy armed ships",}, --["Destroyers"] = {"Heavy armed ships",},
@@ -743,7 +768,6 @@ function UNIT:GetThreatLevel()
end end
end end
self:T2( ThreatLevel )
return ThreatLevel, ThreatText return ThreatLevel, ThreatText
end end

View File

@@ -21,7 +21,6 @@ Core/Radio.lua
Core/Spawn.lua Core/Spawn.lua
Core/SpawnStatic.lua Core/SpawnStatic.lua
Core/Goal.lua Core/Goal.lua
Core/Cargo.lua
Core/Spot.lua Core/Spot.lua
Wrapper/Object.lua Wrapper/Object.lua
@@ -35,6 +34,12 @@ Wrapper/Static.lua
Wrapper/Airbase.lua Wrapper/Airbase.lua
Wrapper/Scenery.lua Wrapper/Scenery.lua
Cargo/Cargo.lua
Cargo/CargoUnit.lua
Cargo/CargoSlingload.lua
Cargo/CargoCrate.lua
Cargo/CargoGroup.lua
Functional/Scoring.lua Functional/Scoring.lua
Functional/CleanUp.lua Functional/CleanUp.lua
Functional/Movement.lua Functional/Movement.lua
@@ -49,6 +54,9 @@ Functional/Range.lua
Functional/ZoneGoal.lua Functional/ZoneGoal.lua
Functional/ZoneGoalCoalition.lua Functional/ZoneGoalCoalition.lua
Functional/ZoneCaptureCoalition.lua Functional/ZoneCaptureCoalition.lua
Functional/Artillery.lua
Functional/Suppression.lua
Functional/PseudoATC.lua
AI/AI_Balancer.lua AI/AI_Balancer.lua
AI/AI_A2A.lua AI/AI_A2A.lua
@@ -61,6 +69,13 @@ AI/AI_Cap.lua
AI/AI_Cas.lua AI/AI_Cas.lua
AI/AI_Bai.lua AI/AI_Bai.lua
AI/AI_Formation.lua AI/AI_Formation.lua
AI/AI_Cargo_APC.lua
AI/AI_Cargo_Helicopter.lua
AI/AI_Cargo_Airplane.lua
AI/AI_Cargo_Dispatcher.lua
AI/AI_Cargo_Dispatcher_APC.lua
AI/AI_Cargo_Dispatcher_Helicopter.lua
AI/AI_Cargo_Dispatcher_Airplane.lua
Actions/Act_Assign.lua Actions/Act_Assign.lua
Actions/Act_Route.lua Actions/Act_Route.lua
@@ -71,12 +86,16 @@ Tasking/CommandCenter.lua
Tasking/Mission.lua Tasking/Mission.lua
Tasking/Task.lua Tasking/Task.lua
Tasking/TaskInfo.lua Tasking/TaskInfo.lua
Tasking/Task_Manager.lua
Tasking/DetectionManager.lua Tasking/DetectionManager.lua
Tasking/Task_A2G_Dispatcher.lua Tasking/Task_A2G_Dispatcher.lua
Tasking/Task_A2G.lua Tasking/Task_A2G.lua
Tasking/Task_A2A_Dispatcher.lua Tasking/Task_A2A_Dispatcher.lua
Tasking/Task_A2A.lua Tasking/Task_A2A.lua
Tasking/Task_Cargo.lua Tasking/Task_Cargo.lua
Tasking/Task_Cargo_Transport.lua
Tasking/Task_Cargo_CSAR.lua
Tasking/Task_Cargo_Dispatcher.lua
Tasking/TaskZoneCapture.lua Tasking/TaskZoneCapture.lua
Moose.lua Moose.lua