-
Notifications
You must be signed in to change notification settings - Fork 1
/
CSARPersisted.lua
1796 lines (1267 loc) · 55.7 KB
/
CSARPersisted.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
-- CSAR Script for DCS Ciribob - 2015
-- Version 1.9.2 - 23/04/2018
-- DCS 1.5 Compatible - Needs Mist 4.0.55 or higher!
--
-- 4 Options:
-- 0 - No Limit - NO Aircraft disabling or pilot lives
-- 1 - Disable Aircraft when its down - Timeout to reenable aircraft
-- 2 - Disable Aircraft for Pilot when he's shot down -- timeout to reenable pilot for aircraft
-- 3 - Pilot Life Limit - No Aircraft Disabling
csar = {}
-- SETTINGS FOR MISSION DESIGNER vvvvvvvvvvvvvvvvvv
csar.csarUnits = {
"Ka-50 Tarawa",
"Ka-50 Tarawa #001",
"UH-1H Tarawa",
"UH-1H Tarawa",
"UH1 Dallas #002",
"UH1 Dallas #001",
"Ka50 Dallas #002",
"Ka50 Dallas #001",
"Mi-8 Dallas #002",
"Mi-8 Dallas #001",
"SA342L Dallas #001",
"SA342L Dallas #002",
"UH1 Paris DEFENDED #001",
"UH1 Paris DEFENDED #002",
"Ka50 Paris DEFENDED #001",
"Ka50 Paris DEFENDED #002",
"Mi-8 Paris DEFENDED #001",
"Mi-8 Paris DEFENDED #002",
"SA342L Paris DEFENDED #001",
"SA342L Paris DEFENDED #002",
} -- List of all the MEDEVAC _UNIT NAMES_ (the line where it says "Pilot" in the ME)!
csar.bluemash = {
"BlueMASH #1",
"BlueMASH #2",
"BlueMASH #3",
"BlueMASH #4",
"BlueMASH #5",
"BlueMASH #6",
"BlueMASH #7",
"BlueMASH #8",
"BlueMASH #9",
"BlueMASH #10"
} -- The unit that serves as MASH for the blue side
csar.redmash = {
"RedMASH #1",
"RedMASH #2",
"RedMASH #3",
"RedMASH #4",
"RedMASH #5",
"RedMASH #6",
"RedMASH #7",
"RedMASH #8",
"RedMASH #9",
"RedMASH #10"
} -- The unit that serves as MASH for the red side
csar.csarMode = 0
-- 0 - No Limit - NO Aircraft disabling
-- 1 - Disable Aircraft when its down - Timeout to reenable aircraft
-- 2 - Disable Aircraft for Pilot when he's shot down -- timeout to reenable pilot for aircraft
-- 3 - Pilot Life Limit - No Aircraft Disabling -- timeout to reset lives?
csar.maxLives = 900 -- Maximum pilot lives
csar.countCSARCrash = false -- If you set to true, pilot lives count for CSAR and CSAR aircraft will count.
csar.reenableIfCSARCrashes = false -- If a CSAR heli crashes, the pilots are counted as rescued anyway. Set to false to Stop this
-- - I recommend you leave the option on below IF USING MODE 1 otherwise the
-- aircraft will be disabled for the duration of the mission
csar.disableAircraftTimeout = false -- Allow aircraft to be used after 20 minutes if the pilot isnt rescued
csar.disableTimeoutTime = 20 -- Time in minutes for TIMEOUT
csar.destructionHeight = 150 -- height in meters an aircraft will be destroyed at if the aircraft is disabled
csar.enableForAI = false -- set to false to disable AI units from being rescued.
csar.enableForRED = false -- enable for red side
csar.enableForBLUE = true -- enable for blue side
csar.enableSlotBlocking = false -- if set to true, you need to put the csarSlotBlockGameGUI.lua
-- in C:/Users/<YOUR USERNAME>/DCS/Scripts for 1.5 or C:/Users/<YOUR USERNAME>/DCS.openalpha/Scripts for 2.0
-- For missions using FLAGS and this script, the CSAR flags will NOT interfere with your mission :)
csar.bluesmokecolor = 4 -- Color of smokemarker for blue side, 0 is green, 1 is red, 2 is white, 3 is orange and 4 is blue
csar.redsmokecolor = 1 -- Color of smokemarker for red side, 0 is green, 1 is red, 2 is white, 3 is orange and 4 is blue
csar.requestdelay = 2 -- Time in seconds before the survivors will request Medevac
csar.coordtype = 3 -- Use Lat/Long DDM (0), Lat/Long DMS (1), MGRS (2), Bullseye imperial (3) or Bullseye metric (4) for coordinates.
csar.coordaccuracy = 1 -- Precision of the reported coordinates, see MIST-docs at http://wiki.hoggit.us/view/GetMGRSString
-- only applies to _non_ bullseye coords
csar.immortalcrew = true -- Set to true to make wounded crew immortal
csar.invisiblecrew = true -- Set to true to make wounded crew insvisible
csar.messageTime = 30 -- Time to show the intial wounded message for in seconds
csar.loadDistance = 60 -- configure distance for pilot to get in helicopter in meters.
csar.radioSound = "beacon.ogg" -- the name of the sound file to use for the Pilot radio beacons. If this isnt added to the mission BEACONS WONT WORK!
csar.allowFARPRescue = true --allows pilot to be rescued by landing at a FARP or Airbase
-- SETTINGS FOR MISSION DESIGNER ^^^^^^^^^^^^^^^^^^^*
-- ***************************************************************
-- **************** Mission Editor Functions *********************
-- ***************************************************************
-----------------------------------------------------------------
-- Resets all life limits so everyone can spawn again. Usage:
-- csar.resetAllPilotLives()
--
function csar.resetAllPilotLives()
for x, _pilot in pairs(csar.pilotLives) do
trigger.action.setUserFlag("CSAR_PILOT" .. _pilot:gsub('%W', ''), csar.maxLives + 1)
end
csar.pilotLives = {}
env.info("Pilot Lives Reset!")
end
-----------------------------------------------------------------
-- Resets all life limits so everyone can spawn again. Usage:
-- csar.resetAllPilotLives()
--
function csar.resetPilotLife(_playerName)
csar.pilotLives[_playerName] = nil
trigger.action.setUserFlag("CSAR_PILOT" .. _playerName:gsub('%W', ''), csar.maxLives + 1)
env.info("Pilot life Reset!")
end
-- ***************************************************************
-- **************** BE CAREFUL BELOW HERE ************************
-- ***************************************************************
-- Sanity checks of mission designer
assert(mist ~= nil, "\n\n** HEY MISSION-DESIGNER! **\n\nMiST has not been loaded!\n\nMake sure MiST 4.0.57 or higher is running\n*before* running this script!\n")
csar.addedTo = {}
csar.downedPilotCounterRed = 0
csar.downedPilotCounterBlue = 0
csar.woundedGroups = {} -- contains the new group of units
csar.inTransitGroups = {} -- contain a table for each SAR with all units he has with the
-- original name of the killed group
csar.radioBeacons = {}
csar.smokeMarkers = {} -- tracks smoke markers for groups
csar.heliVisibleMessage = {} -- tracks if the first message has been sent of the heli being visible
csar.heliCloseMessage = {} -- tracks heli close message ie heli < 500m distance
csar.radioBeacons = {} -- all current beacons
csar.max_units = 6 --number of pilots that can be carried
csar.currentlyDisabled = {} --stored disabled aircraft
csar.hoverStatus = {} -- tracks status of a helis hover above a downed pilot
csar.pilotDisabled = {} -- tracks what aircraft a pilot is disabled for
csar.pilotLives = {} -- tracks how many lives a pilot has
csar.takenOff = {}
function csar.tableLength(T)
if T == nil then
return 0
end
local count = 0
for _ in pairs(T) do count = count + 1 end
return count
end
function csar.pilotsOnboard(_heliName)
local count = 0
if csar.inTransitGroups[_heliName] then
for _, _group in pairs(csar.inTransitGroups[_heliName]) do
count = count + 1
end
end
return count
end
-- Handles all world events
csar.eventHandler = {}
function csar.eventHandler:onEvent(_event)
local status, err = pcall(function(_event)
if _event == nil or _event.initiator == nil then
return false
elseif _event.id == 3 then -- taken offf
if _event.initiator:getName() then
csar.takenOff[_event.initiator:getName()] = true
end
return true
elseif _event.id == 15 then --player entered unit
if _event.initiator:getName() then
csar.takenOff[_event.initiator:getName()] = nil
end
-- if its a sar heli, re-add check status script
for _, _heliName in pairs(csar.csarUnits) do
if _heliName == _event.initiator:getName() then
-- add back the status script
for _woundedName, _groupInfo in pairs(csar.woundedGroups) do
if _groupInfo.side == _event.initiator:getCoalition() then
--env.info(string.format("Schedule Respawn %s %s",_heliName,_woundedName))
-- queue up script
-- Schedule timer to check when to pop smoke
timer.scheduleFunction(csar.checkWoundedGroupStatus, { _heliName, _woundedName }, timer.getTime() + 5)
end
end
end
end
if _event.initiator:getName() and _event.initiator:getPlayerName() then
env.info("Checking Unit - " .. _event.initiator:getName())
csar.checkDisabledAircraftStatus({ _event.initiator:getName(), _event.initiator:getPlayerName() })
end
return true
elseif (_event.id == 9) then
-- Pilot dead
env.info("Event unit - Pilot Dead")
local _unit = _event.initiator
if _unit == nil then
return -- error!
end
local _coalition = _unit:getCoalition()
if _coalition == 1 and not csar.enableForRED then
return --ignore!
end
if _coalition == 2 and not csar.enableForBLUE then
return --ignore!
end
-- Catch multiple events here?
if csar.takenOff[_event.initiator:getName()] == true or _unit:inAir() then
if csar.doubleEjection(_unit) then
return
end
-- trigger.action.outTextForCoalition(_unit:getCoalition(), "MAYDAY MAYDAY! " .. _unit:getTypeName() .. " went down. No Chute!", 10)
csar.handleEjectOrCrash(_unit, true)
else
env.info("Pilot Hasnt taken off, ignore")
end
return
elseif world.event.S_EVENT_EJECTION == _event.id then
env.info("Event unit - Pilot Ejected")
local _unit = _event.initiator
if _unit == nil then
return -- error!
end
local _coalition = _unit:getCoalition()
if _coalition == 1 and not csar.enableForRED then
return --ignore!
end
if _coalition == 2 and not csar.enableForBLUE then
return --ignore!
end
-- TODO catch ejection on runway?
if csar.enableForAI == false and _unit:getPlayerName() == nil then
return
end
if csar.takenOff[_event.initiator:getName()] ~= true and not _unit:inAir() then
env.info("Pilot Hasnt taken off, ignore")
return -- give up, pilot hasnt taken off
end
if csar.doubleEjection(_unit) then
return
end
local _spawnedGroup = csar.spawnGroup(_unit)
csar.addSpecialParametersToGroup(_spawnedGroup)
-- trigger.action.outTextForCoalition(_unit:getCoalition(), "MAYDAY MAYDAY! " .. _unit:getTypeName() .. " went down. Chute Spotted!", 10)
-- csar.displayMessageToSAR(_heliUnit, "MAYDAY MAYDAY! " .. _unit:getTypeName() .. " went down. Chute Spotted!", 10) --dispolay mayday to csar only
local _freq = csar.generateADFFrequency()
csar.addBeaconToGroup(_spawnedGroup:getName(), _freq)
--handle lives and plane disabling
csar.handleEjectOrCrash(_unit, false)
-- Generate DESCRIPTION text
local _text = " "
if _unit:getPlayerName() ~= nil then
-- _text = "Pilot " .. _unit:getPlayerName() .. " of " .. _unit:getName() .. " - " .. _unit:getTypeName()
_text = "Pilot " .. _unit:getPlayerName() .. " of " .. _unit:getTypeName()
else
_text = "AI Pilot of " .. _unit:getName() .. " - " .. _unit:getTypeName()
end
csar.woundedGroups[_spawnedGroup:getName()] = { side = _spawnedGroup:getCoalition(), originalUnit = _unit:getName(), frequency = _freq, desc = _text, player = _unit:getPlayerName() }
csar.initSARForPilot(_spawnedGroup, _freq)
return true
elseif world.event.S_EVENT_LAND == _event.id then
if _event.initiator:getName() then
csar.takenOff[_event.initiator:getName()] = nil
end
if csar.allowFARPRescue then
--env.info("Landing")
local _unit = _event.initiator
if _unit == nil then
env.info("Unit Nil on Landing")
return -- error!
end
csar.takenOff[_event.initiator:getName()] = nil
local _place = _event.place
if _place == nil then
env.info("Landing Place Nil")
return -- error!
end
-- Coalition == 3 seems to be a bug... unless it means contested?!
if _place:getCoalition() == _unit:getCoalition() or _place:getCoalition() == 0 or _place:getCoalition() == 3 then
csar.rescuePilots(_unit)
--env.info("Rescued")
-- env.info("Rescued by Landing")
else
-- env.info("Cant Rescue ")
env.info(string.format("airfield %d, unit %d", _place:getCoalition(), _unit:getCoalition()))
end
end
return true
end
end, _event)
if (not status) then
env.error(string.format("Error while handling event %s", err), false)
end
end
csar.lastCrash = {}
function csar.doubleEjection(_unit)
if csar.lastCrash[_unit:getName()] then
local _time = csar.lastCrash[_unit:getName()]
if timer.getTime() - _time < 10 then
env.info("Caught double ejection!")
return true
end
end
csar.lastCrash[_unit:getName()] = timer.getTime()
return false
end
function csar.handleEjectOrCrash(_unit, _crashed)
-- disable aircraft for ALL pilots
if csar.csarMode == 1 then
if csar.currentlyDisabled[_unit:getName()] ~= nil then
return --already ejected once!
end
-- --mark plane as broken and unflyable
if _unit:getPlayerName() ~= nil and csar.currentlyDisabled[_unit:getName()] == nil then
-- REMOVED CSAR aircraft protection
csar.currentlyDisabled[_unit:getName()] = { timeout = (csar.disableTimeoutTime * 60) + timer.getTime(), desc = "", noPilot = _crashed, unitId = _unit:getID(), name = _unit:getName() }
-- disable aircraft
trigger.action.setUserFlag("CSAR_AIRCRAFT" .. _unit:getID(), 100)
env.info("Unit Disabled: " .. _unit:getName() .. " ID:" .. _unit:getID())
end
--PPERSIST CSAR into a file
CSARSlotDisabledEvent(csar.currentlyDisabled, _unit:getName(), _unit:getPlayerName())
elseif csar.csarMode == 2 then -- disable aircraft for pilot
--csar.pilotDisabled
if _unit:getPlayerName() ~= nil and csar.pilotDisabled[_unit:getPlayerName() .. "_" .. _unit:getName()] == nil then
if csar.countCSARCrash == false then
for _, _heliName in pairs(csar.csarUnits) do
if _unit:getName() == _heliName then
-- IGNORE Crashed CSAR
return
end
end
end
csar.pilotDisabled[_unit:getPlayerName() .. "_" .. _unit:getName()] = { timeout = (csar.disableTimeoutTime * 60) + timer.getTime(), desc = "", noPilot = true, unitId = _unit:getID(), player = _unit:getPlayerName(), name = _unit:getName() }
-- disable aircraft
-- strip special characters from name gsub('%W','')
trigger.action.setUserFlag("CSAR_AIRCRAFT" .. _unit:getPlayerName():gsub('%W', '') .. "_" .. _unit:getID(), 100)
env.info("Unit Disabled for player : " .. _unit:getName())
end
elseif csar.csarMode == 3 then -- No Disable - Just reduce player lives
--csar.pilotDisabled
if _unit:getPlayerName() ~= nil then
if csar.countCSARCrash == false then
for _, _heliName in pairs(csar.csarUnits) do
if _unit:getName() == _heliName then
-- IGNORE Crashed CSAR
return
end
end
end
local _lives = csar.pilotLives[_unit:getPlayerName()]
if _lives == nil then
_lives = csar.maxLives + 1 --plus 1 because we'll use flag set to 1 to indicate NO MORE LIVES
end
csar.pilotLives[_unit:getPlayerName()] = _lives - 1
trigger.action.setUserFlag("CSAR_PILOT" .. _unit:getPlayerName():gsub('%W', ''), _lives - 1)
end
end
end
function csar.enableAircraft(_name, _playerName)
-- enable aircraft for ALL pilots
if csar.csarMode == 1 then
local _details = csar.currentlyDisabled[_name]
if _details ~= nil then
csar.currentlyDisabled[_name] = nil -- {timeout = (csar.disableTimeoutTime*60) + timer.getTime(),desc="",noPilot = _crashed,unitId=_unit:getID() }
CSARSlotEnabledEvent(csar.currentlyDisabled, _name, _playerName)
--use flag to reenable
trigger.action.setUserFlag("CSAR_AIRCRAFT" .. _details.unitId, 0)
end
elseif csar.csarMode == 2 and _playerName ~= nil then -- enable aircraft for pilot
local _details = csar.pilotDisabled[_playerName .. "_" .. _name]
if _details ~= nil then
csar.pilotDisabled[_playerName .. "_" .. _name] = nil
trigger.action.setUserFlag("CSAR_AIRCRAFT" .. _playerName:gsub('%W', '') .. "_" .. _details.unitId, 0)
end
elseif csar.csarMode == 3 and _playerName ~= nil then -- No Disable - Just reduce player lives
-- give back life
local _lives = csar.pilotLives[_playerName]
if _lives == nil then
_lives = csar.maxLives + 1 --plus 1 because we'll use flag set to 1 to indicate NO MORE LIVES
else
_lives = _lives + 1 -- give back live!
if csar.maxLives + 1 <= _lives then
_lives = csar.maxLives + 1 --plus 1 because we'll use flag set to 1 to indicate NO MORE LIVES
end
end
csar.pilotLives[_playerName] = _lives
trigger.action.setUserFlag("CSAR_PILOT" .. _playerName:gsub('%W', ''), _lives)
end
end
function csar.reactivateAircraft()
timer.scheduleFunction(csar.reactivateAircraft, nil, timer.getTime() + 5)
-- disable aircraft for ALL pilots
if csar.csarMode == 1 then
for _unitName, _details in pairs(csar.currentlyDisabled) do
if timer.getTime() >= _details.timeout then
env.info("CSARPERSISTED: reactivate aircraft")
csar.enableAircraft(_unitName)
end
end
elseif csar.csarMode == 2 then -- disable aircraft for pilot
for _key, _details in pairs(csar.pilotDisabled) do
if timer.getTime() >= _details.timeout then
csar.enableAircraft(_details.name, _details.player)
end
end
elseif csar.csarMode == 3 then -- No Disable - Just reduce player lives
end
end
function csar.checkDisabledAircraftStatus(_args)
local _name = _args[1]
local _playerName = _args[2]
local _unit = Unit.getByName(_name)
--if its not the same user anymore, stop checking
if _unit ~= nil and _unit:getPlayerName() ~= nil and _playerName == _unit:getPlayerName() then
-- disable aircraft for ALL pilots
if csar.csarMode == 1 then
local _details = csar.currentlyDisabled[_unit:getName()]
if _details ~= nil then
local _time = _details.timeout - timer.getTime()
if _details.noPilot then
if csar.disableAircraftTimeout then
local _text = string.format("This aircraft cannot be flow as the pilot was killed in a crash. Reinforcements in %.2dM,%.2dS\n\nIt will be DESTROYED on takeoff!", (_time / 60), _time % 60)
--display message,
csar.displayMessageToSAR(_unit, _text, 10, true)
else
--display message,
csar.displayMessageToSAR(_unit, "This aircraft cannot be flown again as the pilot was killed in a crash\n\nIt will be DESTROYED on takeoff!", 10, true)
end
else
if csar.disableAircraftTimeout then
--display message,
csar.displayMessageToSAR(_unit, _details.desc .. " needs to be rescued or reinforcements arrive before this aircraft can be flown again! Reinforcements in " .. string.format("%.2dM,%.2d", (_time / 60), _time % 60) .. "\n\nIt will be DESTROYED on takeoff!", 10, true)
else
--display message,
csar.displayMessageToSAR(_unit, _details.desc .. " needs to be rescued before this aircraft can be flown again!\n\nIt will be DESTROYED on takeoff!", 10, true)
end
end
if csar.destroyUnit(_unit) then
return --plane destroyed
else
--check again in 10 seconds
timer.scheduleFunction(csar.checkDisabledAircraftStatus, _args, timer.getTime() + 10)
end
end
elseif csar.csarMode == 2 then -- disable aircraft for pilot
local _details = csar.pilotDisabled[_unit:getPlayerName() .. "_" .. _unit:getName()]
if _details ~= nil then
local _time = _details.timeout - timer.getTime()
if _details.noPilot then
if csar.disableAircraftTimeout then
local _text = string.format("This aircraft cannot be flow as the pilot was killed in a crash. Reinforcements in %.2dM,%.2dS\n\nIt will be DESTROYED on takeoff!", (_time / 60), _time % 60)
--display message,
csar.displayMessageToSAR(_unit, _text, 10, true)
else
--display message,
csar.displayMessageToSAR(_unit, "This aircraft cannot be flown again as the pilot was killed in a crash\n\nIt will be DESTROYED on takeoff!", 10, true)
end
else
if csar.disableAircraftTimeout then
--display message,
csar.displayMessageToSAR(_unit, _details.desc .. " needs to be rescued or reinforcements arrive before this aircraft can be flown again! Reinforcements in " .. string.format("%.2dM,%.2d", (_time / 60), _time % 60) .. "\n\nIt will be DESTROYED on takeoff!", 10, true)
else
--display message,
csar.displayMessageToSAR(_unit, _details.desc .. " needs to be rescued before this aircraft can be flown again!\n\nIt will be DESTROYED on takeoff!", 10, true)
end
end
if csar.destroyUnit(_unit) then
return --plane destroyed
else
--check again in 10 seconds
timer.scheduleFunction(csar.checkDisabledAircraftStatus, _args, timer.getTime() + 10)
end
end
elseif csar.csarMode == 3 then -- No Disable - Just reduce player lives
local _lives = csar.pilotLives[_unit:getPlayerName()]
if _lives == nil or _lives > 1 then
if _lives == nil then
_lives = csar.maxLives + 1
end
-- -1 for lives as we use 1 to indicate out of lives!
local _text = string.format("CSAR ACTIVE! \n\nYou have " .. (_lives - 1) .. " lives remaining. Make sure you eject!")
csar.displayMessageToSAR(_unit, _text, 20, true)
return
else
local _text = string.format("You have run out of LIVES! Lives will be reset on mission restart or when your pilot is rescued.\n\nThis aircraft will be DESTROYED on takeoff!")
--display message,
csar.displayMessageToSAR(_unit, _text, 10, true)
if csar.destroyUnit(_unit) then
return --plane destroyed
else
--check again in 10 seconds
timer.scheduleFunction(csar.checkDisabledAircraftStatus, _args, timer.getTime() + 10)
end
end
end
end
end
function csar.destroyUnit(_unit)
--destroy if the SAME player is still in the aircraft
-- if a new player got in it'll be destroyed in a bit anyways
if _unit ~= nil and _unit:getPlayerName() ~= nil then
if csar.heightDiff(_unit) > csar.destructionHeight then
csar.displayMessageToSAR(_unit, "**** Aircraft Destroyed as the pilot needs to be rescued or you have no lives! ****", 10, true)
--if we're off the ground then explode
trigger.action.explosion(_unit:getPoint(), 100);
return true
end
--_unit:destroy() destroy doesnt work for playes who arent the host in multiplayer
end
return false
end
function csar.heightDiff(_unit)
local _point = _unit:getPoint()
return _point.y - land.getHeight({ x = _point.x, y = _point.z })
end
csar.addBeaconToGroup = function(_woundedGroupName, _freq)
local _group = Group.getByName(_woundedGroupName)
if _group == nil then
--return frequency to pool of available
for _i, _current in ipairs(csar.usedVHFFrequencies) do
if _current == _freq then
table.insert(csar.freeVHFFrequencies, _freq)
table.remove(csar.usedVHFFrequencies, _i)
end
end
return
end
local _sound = "l10n/DEFAULT/" .. csar.radioSound
trigger.action.radioTransmission(_sound, _group:getUnit(1):getPoint(), 0, false, _freq, 1000)
timer.scheduleFunction(csar.refreshRadioBeacon, { _woundedGroupName, _freq }, timer.getTime() + 30)
end
csar.refreshRadioBeacon = function(_args)
csar.addBeaconToGroup(_args[1], _args[2])
end
csar.addSpecialParametersToGroup = function(_spawnedGroup)
-- Immortal code for alexej21
local _setImmortal = {
id = 'SetImmortal',
params = {
value = true
}
}
-- invisible to AI, Shagrat
local _setInvisible = {
id = 'SetInvisible',
params = {
value = true
}
}
local _controller = _spawnedGroup:getController()
if (csar.immortalcrew) then
Controller.setCommand(_controller, _setImmortal)
end
if (csar.invisiblecrew) then
Controller.setCommand(_controller, _setInvisible)
end
end
function csar.spawnGroup(_deadUnit)
local _id = mist.getNextGroupId()
local _groupName = "Downed Pilot #" .. _id
local _side = _deadUnit:getCoalition()
local _pos = _deadUnit:getPoint()
local _group = {
["visible"] = false,
["groupId"] = _id,
["hidden"] = false,
["units"] = {},
["name"] = _groupName,
["task"] = {},
}
if _side == 2 then
_group.units[1] = csar.createUnit(_pos.x + 50, _pos.z + 50, 120, "Soldier M4")
else
_group.units[1] = csar.createUnit(_pos.x + 50, _pos.z + 50, 120, "Infantry AK")
end
_group.category = Group.Category.GROUND;
_group.country = _deadUnit:getCountry();
local _spawnedGroup = Group.getByName(mist.dynAdd(_group).name)
--deletes spawned group if outside zone
-- Turn off AI
trigger.action.setGroupAIOff(_spawnedGroup)
return _spawnedGroup
end
function csar.createUnit(_x, _y, _heading, _type)
local _id = mist.getNextUnitId();
local _name = string.format("Wounded Pilot #%s", _id)
local _newUnit = {
["y"] = _y,
["type"] = _type,
["name"] = _name,
["unitId"] = _id,
["heading"] = _heading,
["playerCanDrive"] = false,
["skill"] = "Excellent",
["x"] = _x,
}
return _newUnit
end
function csar.initSARForPilot(_downedGroup, _freq)
local _leader = _downedGroup:getUnit(1)
local _coordinatesText = csar.getPositionOfWounded(_downedGroup)
local
_text = string.format("%s requests SAR at %s, beacon at %.2f KHz",
_leader:getName(), _coordinatesText, _freq / 1000)
local _randPercent = math.random(1, 100)
-- Loop through all the medevac units
for x, _heliName in pairs(csar.csarUnits) do
local _status, _err = pcall(function(_args)
local _unitName = _args[1]
local _woundedSide = _args[2]
local _medevacText = _args[3]
local _leaderPos = _args[4]
local _groupName = _args[5]
local _group = _args[6]
local _heli = csar.getSARHeli(_unitName)
-- queue up for all SAR, alive or dead, we dont know the side if they're dead or not spawned so check
--coalition in scheduled smoke
if _heli ~= nil then
-- Check coalition side
if (_woundedSide == _heli:getCoalition()) then
-- Display a delayed message
timer.scheduleFunction(csar.delayedHelpMessage, { _unitName, _medevacText, _groupName }, timer.getTime() + csar.requestdelay)
-- Schedule timer to check when to pop smoke
timer.scheduleFunction(csar.checkWoundedGroupStatus, { _unitName, _groupName }, timer.getTime() + 1)
end
else
--env.warning(string.format("Medevac unit %s not active", _heliName), false)
-- Schedule timer for Dead unit so when the unit respawns he can still pickup units
--timer.scheduleFunction(medevac.checkStatus, {_unitName,_groupName}, timer.getTime() + 5)
end
end, { _heliName, _leader:getCoalition(), _text, _leader:getPoint(), _downedGroup:getName(), _downedGroup })
if (not _status) then
env.warning(string.format("Error while checking with medevac-units %s", _err))
end
end
end
function csar.checkWoundedGroupStatus(_argument)
local _status, _err = pcall(function(_args)
local _heliName = _args[1]
local _woundedGroupName = _args[2]
local _woundedGroup = csar.getWoundedGroup(_woundedGroupName)
local _heliUnit = csar.getSARHeli(_heliName)
-- if wounded group is not here then message alread been sent to SARs
-- stop processing any further
if csar.woundedGroups[_woundedGroupName] == nil then
return
end
if _heliUnit == nil then
-- stop wounded moving, head back to smoke as target heli is DEAD
-- in transit cleanup
-- csar.inTransitGroups[_heliName] = nil
return
end
-- double check that this function hasnt been queued for the wrong side
if csar.woundedGroups[_woundedGroupName].side ~= _heliUnit:getCoalition() then
return --wrong side!
end
if csar.checkGroupNotKIA(_woundedGroup, _woundedGroupName, _heliUnit, _heliName) then
local _woundedLeader = _woundedGroup[1]
local _lookupKeyHeli = _heliUnit:getID() .. "_" .. _woundedLeader:getID() --lookup key for message state tracking
local _distance = csar.getDistance(_heliUnit:getPoint(), _woundedLeader:getPoint())
if _distance < 3000 then
if csar.checkCloseWoundedGroup(_distance, _heliUnit, _heliName, _woundedGroup, _woundedGroupName) == true then
-- we're close, reschedule
timer.scheduleFunction(csar.checkWoundedGroupStatus, _args, timer.getTime() + 1)
end
else
csar.heliVisibleMessage[_lookupKeyHeli] = nil
--reschedule as units arent dead yet , schedule for a bit slower though as we're far away
timer.scheduleFunction(csar.checkWoundedGroupStatus, _args, timer.getTime() + 5)
end
end
end, _argument)
if not _status then
env.error(string.format("error checkWoundedGroupStatus %s", _err))
end
end
function csar.popSmokeForGroup(_woundedGroupName, _woundedLeader)
-- have we popped smoke already in the last 5 mins
local _lastSmoke = csar.smokeMarkers[_woundedGroupName]
if _lastSmoke == nil or timer.getTime() > _lastSmoke then
local _smokecolor
if (_woundedLeader:getCoalition() == 2) then
_smokecolor = csar.bluesmokecolor
else
_smokecolor = csar.redsmokecolor
end
trigger.action.smoke(_woundedLeader:getPoint(), _smokecolor)
csar.smokeMarkers[_woundedGroupName] = timer.getTime() + 300 -- next smoke time
end
end
function csar.pickupUnit(_heliUnit, _pilotName, _woundedGroup, _woundedGroupName)
local _woundedLeader = _woundedGroup[1]
-- GET IN!
local _heliName = _heliUnit:getName()
local _groups = csar.inTransitGroups[_heliName]
local _unitsInHelicopter = csar.pilotsOnboard(_heliName)
-- init table if there is none for this helicopter
if not _groups then
csar.inTransitGroups[_heliName] = {}
_groups = csar.inTransitGroups[_heliName]
end
-- if the heli can't pick them up, show a message and return
if _unitsInHelicopter + 1 > csar.max_units then
csar.displayMessageToSAR(_heliUnit, string.format("%s, %s. We're already crammed with %d guys! Sorry!",
_pilotName, _heliName, _unitsInHelicopter, _unitsInHelicopter), 10)
return true
end
csar.inTransitGroups[_heliName][_woundedGroupName] =
{
originalUnit = csar.woundedGroups[_woundedGroupName].originalUnit,
woundedGroup = _woundedGroupName,
side = _heliUnit:getCoalition(),
desc = csar.woundedGroups[_woundedGroupName].desc,
player = csar.woundedGroups[_woundedGroupName].player,