-
Notifications
You must be signed in to change notification settings - Fork 182
Expand file tree
/
Copy pathmissionparse.cpp
More file actions
9811 lines (8161 loc) · 338 KB
/
Copy pathmissionparse.cpp
File metadata and controls
9811 lines (8161 loc) · 338 KB
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
/*
* Copyright (C) Volition, Inc. 1999. All rights reserved.
*
* All source code herein is the property of Volition, Inc. You may not sell
* or otherwise commercially exploit the source or things you created based on the
* source.
*
*/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cassert>
#include <cstdarg>
#include <csetjmp>
#include "ai/aigoals.h"
#include "ai/ailua.h"
#include "asteroid/asteroid.h"
#include "bmpman/bmpman.h"
#include "cfile/cfile.h"
#include "cmdline/cmdline.h"
#include "debris/debris.h"
#include "gamesnd/eventmusic.h"
#include "globalincs/alphacolors.h"
#include "globalincs/linklist.h"
#include "hud/hud.h"
#include "hud/hudescort.h"
#include "hud/hudets.h"
#include "hud/hudsquadmsg.h"
#include "hud/hudwingmanstatus.h"
#include "iff_defs/iff_defs.h"
#include "io/timer.h"
#include "jumpnode/jumpnode.h"
#include "lighting/lighting.h"
#include "lighting/lighting_profiles.h"
#include "localization/localize.h"
#include "math/bitarray.h"
#include "math/fvi.h"
#include "math/staticrand.h"
#include "mission/missionbriefcommon.h"
#include "mission/missioncampaign.h"
#include "mission/missiongoals.h"
#include "mission/missionhotkey.h"
#include "mission/missionlog.h"
#include "mission/missionmessage.h"
#include "mission/missionparse.h"
#include "missionui/fictionviewer.h"
#include "missionui/missioncmdbrief.h"
#include "missionui/redalert.h"
#include "mod_table/mod_table.h"
#include "nebula/neb.h"
#include "nebula/neblightning.h"
#include "network/multi.h"
#include "network/multi_endgame.h"
#include "network/multi_respawn.h"
#include "network/multimsgs.h"
#include "network/multiutil.h"
#include "object/objectdock.h"
#include "object/parseobjectdock.h"
#include "object/objectshield.h"
#include "object/waypoint.h"
#include "parse/generic_log.h"
#include "parse/parselo.h"
#include "parse/sexp_container.h"
#include "prop/prop.h"
#include "scripting/global_hooks.h"
#include "scripting/hook_api.h"
#include "scripting/hook_conditions.h"
#include "scripting/scripting.h"
#include "species_defs/species_defs.h"
#include "playerman/player.h"
#include "popup/popup.h"
#include "popup/popupdead.h"
#include "ship/ship.h"
#include "ship/shipfx.h"
#include "ship/shiphit.h"
#include "sound/ds.h"
#include "starfield/nebula.h"
#include "starfield/starfield.h"
#include "weapon/weapon.h"
#include "tracing/Monitor.h"
#include "missionparse.h"
// MISSION_VERSION should be the earliest version of FSO that can load the current mission format without
// requiring version-specific comments. It should be updated whenever the format changes, but it should
// not be updated simply because the engine's version changed.
// NOTE: The version can only have two numbers because old FRED builds expect the version to be a float.
const gameversion::version MISSION_VERSION = gameversion::version(23, 1);
const gameversion::version LEGACY_MISSION_VERSION = gameversion::version(0, 10);
LOCAL struct {
char docker[NAME_LENGTH];
char dockee[NAME_LENGTH];
char docker_point[NAME_LENGTH];
char dockee_point[NAME_LENGTH];
} Initially_docked[MAX_SHIPS];
int Total_initially_docked;
// coverity[GLOBAL_INIT_ORDER] -- safe; default-constructed, no cross-TU dependencies
mission The_mission;
char Mission_filename[80];
int Mission_palette; // index into Nebula_palette_filenames[] of palette file to use for mission
int Nebula_index; // index into Nebula_filenames[] of nebula to use in mission.
int Num_ai_behaviors = MAX_AI_BEHAVIORS;
int Num_cargo = 0;
int Num_arrival_names = MAX_ARRIVAL_NAMES;
int Num_goal_type_names = MAX_GOAL_TYPE_NAMES;
int Num_parse_goals;
int Player_starts = 1;
int Num_teams;
fix Entry_delay_time = 0;
int Num_unknown_ship_classes;
int Num_unknown_prop_classes;
int Num_unknown_weapon_classes;
int Num_unknown_loadout_classes;
ushort Current_file_checksum = 0;
ushort Last_file_checksum = 0;
int Current_file_length = 0;
// coverity[GLOBAL_INIT_ORDER] -- safe; default-constructed, no cross-TU dependencies
SCP_vector<mission_default_custom_data> Default_custom_data;
// alternate ship type names
char Mission_alt_types[MAX_ALT_TYPE_NAMES][NAME_LENGTH];
int Mission_alt_type_count = 0;
// callsigns
char Mission_callsigns[MAX_CALLSIGNS][NAME_LENGTH];
int Mission_callsign_count = 0;
#define SHIP_WARP_TIME 5.0f // how many seconds it takes for ship to warp in
// the ship arrival list will contain a list of ships that are yet to arrive. This
// list could also include ships that are part of wings!
// coverity[GLOBAL_INIT_ORDER] -- safe; default-constructed, no cross-TU dependencies
p_object Ship_arrival_list; // for linked list of ships to arrive later
// all the ships that we parse
// coverity[GLOBAL_INIT_ORDER] -- safe; default-constructed, no cross-TU dependencies
SCP_vector<p_object> Parse_objects;
// all the props that we parse
SCP_vector<parsed_prop> Parse_props;
// list for arriving support ship
// coverity[GLOBAL_INIT_ORDER] -- safe; default-constructed, no cross-TU dependencies
p_object Support_ship_pobj;
p_object *Arriving_support_ship;
char Arriving_repair_targets[MAX_AI_GOALS][NAME_LENGTH];
int Num_arriving_repair_targets;
#define MIN_SUBSYS_STATUS_SIZE 25
subsys_status *Subsys_status = NULL;
int Subsys_index;
int Subsys_status_size;
char Mission_parse_storm_name[NAME_LENGTH] = "none";
team_data Team_data[MAX_TVT_TEAMS];
// variables for player start in single player
char Player_start_shipname[NAME_LENGTH];
int Player_start_shipnum;
p_object *Player_start_pobject;
// name of all ships to use while parsing a mission (since a ship might be referenced by
// something before that ship has even been loaded yet)
SCP_vector<SCP_string> Parse_names;
SCP_vector<SCP_string> Mission_parse_warnings;
// Routes a parse-time auto-correction notice to the right surface for the app:
// outside QtFRED, the existing Warning(LOCATION, ...) popup; inside QtFRED, the
// Mission_parse_warnings queue so the ErrorChecker can present it without a popup.
static void parse_warning_or_record(SCP_FORMAT_STRING const char* fmt, ...) SCP_FORMAT_STRING_ARGS(1, 2);
static void parse_warning_or_record(const char* fmt, ...)
{
SCP_string msg;
va_list args;
va_start(args, fmt);
vsprintf(msg, fmt, args);
va_end(args);
if (Qtfred_running) {
Mission_parse_warnings.push_back(std::move(msg));
} else {
Warning(LOCATION, "%s", msg.c_str());
}
}
SCP_vector<texture_replace> Fred_texture_replacements;
SCP_unordered_set<int> Fred_migrated_immobile_ships;
int Num_path_restrictions;
path_restriction_t Path_restrictions[MAX_PATH_RESTRICTIONS];
constexpr int DEFAULT_LARGE_SHIP_NO_COLLIDE_COLLISION_GROUP = 0;
extern int debrief_find_persona_index();
static bool mission_has_layer_name(const mission* pm, const SCP_string& layerName) {
return std::any_of(pm->fred_layers.begin(), pm->fred_layers.end(), [&layerName](const SCP_string& existingLayer) {
return stricmp(existingLayer.c_str(), layerName.c_str()) == 0;
});
}
//XSTR:OFF
const char *Nebula_filenames[NUM_NEBULAS] = {
"Nebula01",
"Nebula02",
"Nebula03"
};
const char *Neb2_filenames[NUM_NEBULAS] = {
"Nebfull01",
"Nebfull02",
"Nebfull03"
};
// Note: Nebula_colors[] and Nebula_palette_filenames are linked via index numbers
const char *Nebula_colors[NUM_NEBULA_COLORS] = {
"Red",
"Blue",
"Gold",
"Purple",
"Maroon",
"Green",
"Grey blue",
"Violet",
"Grey Green",
};
const char *Ai_behavior_names[MAX_AI_BEHAVIORS] = {
"Chase",
"Evade",
"Get behind",
"Stay Near",
"Still",
"Guard",
"Avoid",
"Waypoints",
"Dock",
"None",
"Big Ship",
"Path",
"Be Rearmed",
"Safety",
"Evade Weapon",
"Strafe",
"Play Dead",
"Bay Emerge",
"Bay Depart",
"Sentry Gun",
"Warp Out",
"Fly To Ship",
"Lua AI",
};
char *Cargo_names[MAX_CARGO];
char Cargo_names_buf[MAX_CARGO][NAME_LENGTH];
const char *Ship_class_names[MAX_SHIP_CLASSES]; // to be filled in from Ship_info array
const char *Icon_names[MIN_BRIEF_ICONS] = {
"Fighter", "Fighter Wing", "Cargo", "Cargo Wing", "Largeship",
"Largeship Wing", "Capital", "Planet", "Asteroid Field", "Waypoint",
"Support Ship", "Freighter(no cargo)", "Freighter(has cargo)",
"Freighter Wing(no cargo)", "Freighter Wing(has cargo)", "Installation",
"Bomber", "Bomber Wing", "Cruiser", "Cruiser Wing", "Unknown", "Unknown Wing",
"Player Fighter", "Player Fighter Wing", "Player Bomber", "Player Bomber Wing",
"Knossos Device", "Transport Wing", "Corvette", "Gas Miner", "Awacs", "Supercap", "Sentry Gun", "Jump Node", "Transport"
};
// definitions for arrival locations for ships/wings
const char *Arrival_location_names[MAX_ARRIVAL_NAMES] = {
"Hyperspace", "Near Ship", "In front of ship", "In back of ship", "Above ship", "Below ship", "To left of ship", "To right of ship", "Docking Bay",
};
const char *Departure_location_names[MAX_DEPARTURE_NAMES] = {
"Hyperspace", "Docking Bay",
};
const char *Goal_type_names[MAX_GOAL_TYPE_NAMES] = {
"Primary", "Secondary", "Bonus",
};
const char *Reinforcement_type_names[] = {
"Attack/Protect",
"Repair/Rearm",
};
const char *Old_game_types[OLD_MAX_GAME_TYPES] = {
"Single Player Only",
"Multiplayer Only",
"Single/Multi Player",
"Training mission"
};
flag_def_list_new<Ship::Ship_Flags> Parse_ship_flags[] = {
{"cargo-known", Ship::Ship_Flags::Cargo_revealed, true, false},
{"ignore-count", Ship::Ship_Flags::Ignore_count, true, false},
{"reinforcement", Ship::Ship_Flags::Reinforcement, true, false},
{"escort", Ship::Ship_Flags::Escort, true, false},
{"no-arrival-music", Ship::Ship_Flags::No_arrival_music, true, false},
{"no-arrival-warp", Ship::Ship_Flags::No_arrival_warp, true, false},
{"no-departure-warp", Ship::Ship_Flags::No_departure_warp, true, false},
{"hidden-from-sensors", Ship::Ship_Flags::Hidden_from_sensors, true, false},
{"scannable", Ship::Ship_Flags::Scannable, true, false},
{"red-alert-carry", Ship::Ship_Flags::Red_alert_store_status, true, false},
{"vaporize", Ship::Ship_Flags::Vaporize, true, false},
{"stealth", Ship::Ship_Flags::Stealth, true, false},
{"friendly-stealth-invisible", Ship::Ship_Flags::Friendly_stealth_invis, true, false},
{"don't-collide-invisible", Ship::Ship_Flags::Dont_collide_invis, true, false},
{"primitive-sensors", Ship::Ship_Flags::Primitive_sensors, true, false},
{"no-subspace-drive", Ship::Ship_Flags::No_subspace_drive, true, false},
{"nav-carry-status", Ship::Ship_Flags::Navpoint_carry, true, false},
{"affected-by-gravity", Ship::Ship_Flags::Affected_by_gravity, true, false},
{"toggle-subsystem-scanning", Ship::Ship_Flags::Toggle_subsystem_scanning, true, false},
{"no-builtin-messages", Ship::Ship_Flags::No_builtin_messages, true, false},
{"primaries-locked", Ship::Ship_Flags::Primaries_locked, true, false},
{"secondaries-locked", Ship::Ship_Flags::Secondaries_locked, true, false},
{"no-death-scream", Ship::Ship_Flags::No_death_scream, true, false},
{"always-death-scream", Ship::Ship_Flags::Always_death_scream, true, false},
{"nav-needslink", Ship::Ship_Flags::Navpoint_needslink, true, false},
{"hide-ship-name", Ship::Ship_Flags::Hide_ship_name, true, false},
{"set-class-dynamically", Ship::Ship_Flags::Set_class_dynamically, true, false},
{"lock-all-turrets", Ship::Ship_Flags::Lock_all_turrets_initially, true, false},
{"afterburners-locked", Ship::Ship_Flags::Afterburner_locked, true, false},
{"no-ets", Ship::Ship_Flags::No_ets, true, false},
{"cloaked", Ship::Ship_Flags::Cloaked, true, false},
{"ship-locked", Ship::Ship_Flags::Ship_locked, true, false},
{"weapons-locked", Ship::Ship_Flags::Weapons_locked, true, false},
{"scramble-messages", Ship::Ship_Flags::Scramble_messages, true, false},
{"no-disabled-self-destruct", Ship::Ship_Flags::No_disabled_self_destruct, true, false},
{"hide-in-mission-log", Ship::Ship_Flags::Hide_mission_log, true, false},
{"same-arrival-warp-when-docked", Ship::Ship_Flags::Same_arrival_warp_when_docked, true, false},
{"same-departure-warp-when-docked", Ship::Ship_Flags::Same_departure_warp_when_docked, true, false},
{"fail-sound-locked-primary", Ship::Ship_Flags::Fail_sound_locked_primary, true, false},
{"fail-sound-locked-secondary", Ship::Ship_Flags::Fail_sound_locked_secondary, true, false},
{"aspect-immune", Ship::Ship_Flags::Aspect_immune, true, false},
{"cannot-perform-scan-hide-cargo", Ship::Ship_Flags::Cannot_perform_scan_hide_cargo, true, false},
{"cannot-perform-scan-show-cargo", Ship::Ship_Flags::Cannot_perform_scan_show_cargo, true, false},
{"no-targeting-limits", Ship::Ship_Flags::No_targeting_limits, true, false},
{"no-scanned-cargo", Ship::Ship_Flags::No_scanned_cargo, true, false},
{"force-shields-on", Ship::Ship_Flags::Force_shields_on, true, false},
{"Destroy before Mission", Ship::Ship_Flags::Kill_before_mission,true, false}, //Not Printed to misson so can use descriptive name
}
;
const size_t Num_Parse_ship_flags = sizeof(Parse_ship_flags) / sizeof(flag_def_list_new<Ship::Ship_Flags>);
flag_def_list_new<AI::AI_Flags> Parse_ship_ai_flags[] = {
{"kamikaze", AI::AI_Flags::Kamikaze, true, false},
{"no-dynamic", AI::AI_Flags::No_dynamic, true, false},
};
const size_t Num_Parse_ship_ai_flags = sizeof(Parse_ship_ai_flags) / sizeof(flag_def_list_new<AI::AI_Flags>);
flag_def_list_new<Object::Object_Flags> Parse_ship_object_flags[] = {
{"protect-ship", Object::Object_Flags::Protected, true, false},
{"no-shields", Object::Object_Flags::No_shields, true, false},
{"player-start", Object::Object_Flags::Player_ship, true, false},
{"invulnerable", Object::Object_Flags::Invulnerable, true, false},
{"beam-protect-ship", Object::Object_Flags::Beam_protected, true, false},
{"flak-protect-ship", Object::Object_Flags::Flak_protected, true, false},
{"laser-protect-ship", Object::Object_Flags::Laser_protected, true, false},
{"missile-protect-ship", Object::Object_Flags::Missile_protected, true, false},
{"special-warp", Object::Object_Flags::Special_warpin, true, false},
{"targetable-as-bomb", Object::Object_Flags::Targetable_as_bomb, true, false},
{"don't-change-position", Object::Object_Flags::Dont_change_position, true, false},
{"don't-change-orientation", Object::Object_Flags::Dont_change_orientation, true, false},
{"no_collide", Object::Object_Flags::Collides, true, false},
{"ai-attackable-if-no-collide", Object::Object_Flags::Attackable_if_no_collide, true, false},
};
const size_t Num_Parse_ship_object_flags =
sizeof(Parse_ship_object_flags) / sizeof(flag_def_list_new<Object::Object_Flags>);
// These are a little different than the object flags as they aren't used in traditional flag sexps or parsed flag lists
// Instead, this list is used to popuplate QtFRED's mission specs flag checkboxes. As such the names can be more descriptive than other flag def lists
// NOTE: Inactive flags and special flags are not added to the UI flag list. It is assumed that special flags exist in some other UI form
flag_def_list_new<Mission::Mission_Flags> Parse_mission_flags[] = {
{"Mission Takes Place In Subspace", Mission::Mission_Flags::Subspace, true, true},
{"Disallow Promotions/Badges", Mission::Mission_Flags::No_promotion, true, false},
{"Mission Takes Place In Full Nebula", Mission::Mission_Flags::Fullneb, true, true},
{"Disable Built-in Messages", Mission::Mission_Flags::No_builtin_msgs, true, false},
{"No Traitor", Mission::Mission_Flags::No_traitor, true, false},
{"Toggle Ship Trails", Mission::Mission_Flags::Toggle_ship_trails, true, true},
{"Support Ship Repairs Hull", Mission::Mission_Flags::Support_repairs_hull, true, true},
{"All Ships Beam-Freed By Default", Mission::Mission_Flags::Beam_free_all_by_default, true, false},
{"UNUSED 1", Mission::Mission_Flags::Unused_1, false, false},
{"UNUSED 2", Mission::Mission_Flags::Unused_2, false, false},
{"No Briefing", Mission::Mission_Flags::No_briefing, true, false},
{"Toggle Debriefing (On/Off)", Mission::Mission_Flags::Toggle_debriefing, true, false},
{"UNUSED 3", Mission::Mission_Flags::Unused_3, false, false},
{"UNUSED 4", Mission::Mission_Flags::Unused_4, false, false},
{"2D Mission", Mission::Mission_Flags::Mission_2d, true, false},
{"UNUSED 5", Mission::Mission_Flags::Unused_5, false, false},
{"Red Alert Mission", Mission::Mission_Flags::Red_alert, true, false},
{"Scramble Mission", Mission::Mission_Flags::Scramble, true, false},
{"Disable Built-in Command Messages", Mission::Mission_Flags::No_builtin_command, true, false},
{"Player Starts under AI Control (NO MULTI)", Mission::Mission_Flags::Player_start_ai, true, false},
{"All Teams at War", Mission::Mission_Flags::All_attack, true, false},
{"Use Autopilot Cinematics", Mission::Mission_Flags::Use_ap_cinematics, true, false},
{"Deactivate Hardcoded Autopilot", Mission::Mission_Flags::Deactivate_ap, true, false},
{"Toggle Showing Goals In Briefing", Mission::Mission_Flags::Toggle_showing_goals, true, false},
{"Mission End to Mainhall", Mission::Mission_Flags::End_to_mainhall, true, false},
{"Override #Command with Command Info", Mission::Mission_Flags::Override_hashcommand, true, true},
{"Toggle Starting in Chase View", Mission::Mission_Flags::Toggle_start_chase_view, true, false},
{"Nebula Fog Color Override", Mission::Mission_Flags::Neb2_fog_color_override, true, true},
{"Full Nebula Background Bitmaps", Mission::Mission_Flags::Fullneb_background_bitmaps, true, true},
{"Preload Subspace Tunnel", Mission::Mission_Flags::Preload_subspace, true, false},
{"Large Ships Do Not Collide By Default", Mission::Mission_Flags::Large_ships_no_collide_by_default, true, false},
{"Limit Support Rearm to Mission Pool", Mission::Mission_Flags::Limited_support_rearm_pool, true, true}
};
parse_object_flag_description<Mission::Mission_Flags> Parse_mission_flag_descriptions[] = {
{Mission::Mission_Flags::Subspace, "Mission takes place in subspace"},
{Mission::Mission_Flags::No_promotion, "Cannot get promoted or badges in this mission"},
{Mission::Mission_Flags::Fullneb, "Mission is a full nebula mission"},
{Mission::Mission_Flags::No_builtin_msgs, "Disables all builtin messages except Command"},
{Mission::Mission_Flags::No_traitor, "Player cannot become a traitor"},
{Mission::Mission_Flags::Toggle_ship_trails, "Toggles ship trails (off in nebula, on outside nebula)"},
{Mission::Mission_Flags::Support_repairs_hull, "Toggles support ship repair of ship hulls"},
{Mission::Mission_Flags::Beam_free_all_by_default, "All ships are beam-freed by default"},
{Mission::Mission_Flags::Unused_1, "UNUSED 1"}, // Necessary to not break parsing. Is this still true??
{Mission::Mission_Flags::Unused_2, "UNUSED 2"}, // Necessary to not break parsing. Is this still true??
{Mission::Mission_Flags::No_briefing, "No briefing, mission starts immediately"},
{Mission::Mission_Flags::Toggle_debriefing, "Toggles debriefing on for dogfight. Off for everything else"},
{Mission::Mission_Flags::Unused_3, "UNUSED 3"}, // Necessary to not break parsing. Is this still true??
{Mission::Mission_Flags::Unused_4, "UNUSED 4"}, // Necessary to not break parsing. Is this still true??
{Mission::Mission_Flags::Mission_2d, "Mission is meant to be played top-down style; 2D physics and movement."},
{Mission::Mission_Flags::Unused_5, "UNUSED 5"}, // Necessary to not break parsing. Is this still true??
{Mission::Mission_Flags::Red_alert, "A red-alert mission"},
{Mission::Mission_Flags::Scramble, "A scramble mission"},
{Mission::Mission_Flags::No_builtin_command, "Disables builtin Command messages"},
{Mission::Mission_Flags::Player_start_ai, "Player starts mission under AI Control"},
{Mission::Mission_Flags::All_attack, "All teams target each other"},
{Mission::Mission_Flags::Use_ap_cinematics, "Use autopilot cinematics"},
{Mission::Mission_Flags::Deactivate_ap, "Deactivate hardcoded autopilot"},
{Mission::Mission_Flags::Toggle_showing_goals, "Show mission goals for training missions, hide otherwise"},
{Mission::Mission_Flags::End_to_mainhall, "Return to the mainhall after debrief instead of starting the next mission"},
{Mission::Mission_Flags::Override_hashcommand, "Override #Command with the Command info in Mission Specs"},
{Mission::Mission_Flags::Toggle_start_chase_view, "Toggles whether the player starts the mission in chase view"},
{Mission::Mission_Flags::Neb2_fog_color_override, "Whether to use explicit fog colors instead of checking the palette"},
{Mission::Mission_Flags::Fullneb_background_bitmaps, "Show background bitmaps despite full nebula"},
{Mission::Mission_Flags::Preload_subspace, "Preload the subspace tunnel for both the sexp and specs checkbox"},
{Mission::Mission_Flags::Large_ships_no_collide_by_default, "Automatically places all large ships in the configured collision group, preventing large ships from colliding with each other"},
{Mission::Mission_Flags::Limited_support_rearm_pool, "Support ships can only rearm from the mission weapon pool"},
};
const size_t Num_parse_mission_flags = sizeof(Parse_mission_flags) / sizeof(flag_def_list_new<Mission::Mission_Flags>);
const size_t Num_parse_mission_flag_descriptions = sizeof(Parse_mission_flag_descriptions) / sizeof(parse_object_flag_description<Mission::Mission_Flags>);
flag_def_list_new<Mission::Parse_Object_Flags> Parse_object_flags[] = {
{ "cargo-known", Mission::Parse_Object_Flags::SF_Cargo_known, true, false },
{ "ignore-count", Mission::Parse_Object_Flags::SF_Ignore_count, true, false },
{ "protect-ship", Mission::Parse_Object_Flags::OF_Protected, true, false },
{ "reinforcement", Mission::Parse_Object_Flags::SF_Reinforcement, true, false },
{ "no-shields", Mission::Parse_Object_Flags::OF_No_shields, true, false },
{ "escort", Mission::Parse_Object_Flags::SF_Escort, true, false },
{ "player-start", Mission::Parse_Object_Flags::OF_Player_start, true, false },
{ "no-arrival-music", Mission::Parse_Object_Flags::SF_No_arrival_music, true, false },
{ "no-arrival-warp", Mission::Parse_Object_Flags::SF_No_arrival_warp, true, false },
{ "no-departure-warp", Mission::Parse_Object_Flags::SF_No_departure_warp, true, false },
{ "locked", Mission::Parse_Object_Flags::SF_Locked, true, false },
{ "invulnerable", Mission::Parse_Object_Flags::OF_Invulnerable, true, false },
{ "hidden-from-sensors", Mission::Parse_Object_Flags::SF_Hidden_from_sensors, true, false },
{ "scannable", Mission::Parse_Object_Flags::SF_Scannable, true, false },
{ "kamikaze", Mission::Parse_Object_Flags::AIF_Kamikaze, true, false },
{ "no-dynamic", Mission::Parse_Object_Flags::AIF_No_dynamic, true, false },
{ "red-alert-carry", Mission::Parse_Object_Flags::SF_Red_alert_store_status, true, false },
{ "beam-protect-ship", Mission::Parse_Object_Flags::OF_Beam_protected, true, false },
{ "flak-protect-ship", Mission::Parse_Object_Flags::OF_Flak_protected, true, false },
{ "laser-protect-ship", Mission::Parse_Object_Flags::OF_Laser_protected, true, false },
{ "missile-protect-ship", Mission::Parse_Object_Flags::OF_Missile_protected, true, false },
{ "guardian", Mission::Parse_Object_Flags::SF_Guardian, true, false },
{ "special-warp", Mission::Parse_Object_Flags::Knossos_warp_in, true, false },
{ "vaporize", Mission::Parse_Object_Flags::SF_Vaporize, true, false },
{ "stealth", Mission::Parse_Object_Flags::SF_Stealth, true, false },
{ "friendly-stealth-invisible", Mission::Parse_Object_Flags::SF_Friendly_stealth_invis, true, false },
{ "don't-collide-invisible", Mission::Parse_Object_Flags::SF_Dont_collide_invis, true, false },
{ "primitive-sensors", Mission::Parse_Object_Flags::SF_Primitive_sensors, true, false },
{ "no-subspace-drive", Mission::Parse_Object_Flags::SF_No_subspace_drive, true, false },
{ "nav-carry-status", Mission::Parse_Object_Flags::SF_Nav_carry_status, true, false },
{ "affected-by-gravity", Mission::Parse_Object_Flags::SF_Affected_by_gravity, true, false },
{ "toggle-subsystem-scanning", Mission::Parse_Object_Flags::SF_Toggle_subsystem_scanning, true, false },
{ "targetable-as-bomb", Mission::Parse_Object_Flags::OF_Targetable_as_bomb, true, false },
{ "no-builtin-messages", Mission::Parse_Object_Flags::SF_No_builtin_messages, true, false },
{ "primaries-locked", Mission::Parse_Object_Flags::SF_Primaries_locked, true, false },
{ "secondaries-locked", Mission::Parse_Object_Flags::SF_Secondaries_locked, true, false },
{ "no-death-scream", Mission::Parse_Object_Flags::SF_No_death_scream, true, false },
{ "always-death-scream", Mission::Parse_Object_Flags::SF_Always_death_scream, true, false },
{ "nav-needslink", Mission::Parse_Object_Flags::SF_Nav_needslink, true, false },
{ "hide-ship-name", Mission::Parse_Object_Flags::SF_Hide_ship_name, true, false },
{ "set-class-dynamically", Mission::Parse_Object_Flags::SF_Set_class_dynamically, true, false },
{ "lock-all-turrets", Mission::Parse_Object_Flags::SF_Lock_all_turrets_initially, true, false },
{ "afterburners-locked", Mission::Parse_Object_Flags::SF_Afterburner_locked, true, false },
{ "force-shields-on", Mission::Parse_Object_Flags::OF_Force_shields_on, true, false },
{ "immobile", Mission::Parse_Object_Flags::OF_Immobile, true, false },
{ "don't-change-position", Mission::Parse_Object_Flags::OF_Dont_change_position, true, false },
{ "don't-change-orientation", Mission::Parse_Object_Flags::OF_Dont_change_orientation, true, false },
{ "no-ets", Mission::Parse_Object_Flags::SF_No_ets, true, false },
{ "cloaked", Mission::Parse_Object_Flags::SF_Cloaked, true, false },
{ "ship-locked", Mission::Parse_Object_Flags::SF_Ship_locked, true, false },
{ "weapons-locked", Mission::Parse_Object_Flags::SF_Weapons_locked, true, false },
{ "scramble-messages", Mission::Parse_Object_Flags::SF_Scramble_messages, true, false },
{ "no_collide", Mission::Parse_Object_Flags::OF_No_collide, true, false },
{ "no-disabled-self-destruct", Mission::Parse_Object_Flags::SF_No_disabled_self_destruct, true, false },
{ "hide-in-mission-log", Mission::Parse_Object_Flags::SF_Hide_mission_log, true, false },
{ "same-arrival-warp-when-docked", Mission::Parse_Object_Flags::SF_Same_arrival_warp_when_docked, true, false },
{ "same-departure-warp-when-docked", Mission::Parse_Object_Flags::SF_Same_departure_warp_when_docked, true, false },
{ "ai-attackable-if-no-collide", Mission::Parse_Object_Flags::OF_Attackable_if_no_collide, true, false },
{ "fail-sound-locked-primary", Mission::Parse_Object_Flags::SF_Fail_sound_locked_primary, true, false },
{ "fail-sound-locked-secondary", Mission::Parse_Object_Flags::SF_Fail_sound_locked_secondary, true, false },
{ "aspect-immune", Mission::Parse_Object_Flags::SF_Aspect_immune, true, false },
{ "cannot-perform-scan-hide-cargo", Mission::Parse_Object_Flags::SF_Cannot_perform_scan_hide_cargo, true, false },
{ "cannot-perform-scan-show-cargo", Mission::Parse_Object_Flags::SF_Cannot_perform_scan_show_cargo, true, false },
{ "no-targeting-limits", Mission::Parse_Object_Flags::SF_No_targeting_limits, true, false},
{ "no-scanned-cargo", Mission::Parse_Object_Flags::SF_No_scanned_cargo, true, false },
};
parse_object_flag_description<Mission::Parse_Object_Flags> Parse_object_flag_descriptions[] = {
{ Mission::Parse_Object_Flags::SF_Cargo_known, "If set, the ship's cargo can be seen without scanning the ship."},
{ Mission::Parse_Object_Flags::SF_Ignore_count, "Ignore this ship when counting ship types for goals."},
{ Mission::Parse_Object_Flags::OF_Protected, "Ship and Turret AI will ignore and not attack ship."},
{ Mission::Parse_Object_Flags::SF_Reinforcement, "This ship is a reinforcement ship."},
{ Mission::Parse_Object_Flags::OF_No_shields, "Ship will have no shields (ETS will be rebalanced if shields were off and are enabled)."},
{ Mission::Parse_Object_Flags::SF_Escort, "This ship is an escort ship."},
{ Mission::Parse_Object_Flags::OF_Player_start, "Ship is a player ship."},
{ Mission::Parse_Object_Flags::SF_No_arrival_music, "Don't play arrival music when ship arrives."},
{ Mission::Parse_Object_Flags::SF_No_arrival_warp, "No arrival warp-in effect."},
{ Mission::Parse_Object_Flags::SF_No_departure_warp, "No departure warp-in effect."},
{ Mission::Parse_Object_Flags::SF_Locked, "Ship cannot be changed in the Ship Loadout."},
{ Mission::Parse_Object_Flags::OF_Invulnerable, "Stops ship from taking any damage."},
{ Mission::Parse_Object_Flags::SF_Hidden_from_sensors, "If set, the ship can't be targeted and appears on radar as a blinking dot."},
{ Mission::Parse_Object_Flags::SF_Scannable, "Whether or not the ship can be scanned."},
{ Mission::Parse_Object_Flags::AIF_Kamikaze, "Ship will attack big ships by colliding with them and exploding."},
{ Mission::Parse_Object_Flags::AIF_No_dynamic, "Will stop allowing the AI to pursue dynamic goals (eg: chasing ships it was not ordered to)."},
{ Mission::Parse_Object_Flags::SF_Red_alert_store_status, "Ship status should be stored/restored if red alert mission."},
{ Mission::Parse_Object_Flags::OF_Beam_protected, "Turrets with beam weapons will ignore and not attack ship."},
{ Mission::Parse_Object_Flags::OF_Flak_protected, "Turrets with flak weapons will ignore and not attack ship."},
{ Mission::Parse_Object_Flags::OF_Laser_protected, "Turrets with laser weapons will ignore and not attack ship."},
{ Mission::Parse_Object_Flags::OF_Missile_protected, "Turrets with missile weapons will ignore and not attack ship."},
{ Mission::Parse_Object_Flags::SF_Guardian, "Ship health cannot be reduced below 1."},
{ Mission::Parse_Object_Flags::Knossos_warp_in, "Ship uses the special Knossos warp-in animation."},
{ Mission::Parse_Object_Flags::SF_Vaporize, "Causes a ship to vanish (no deathroll, no debris, no explosion) when destroyed."},
{ Mission::Parse_Object_Flags::SF_Stealth, "If set, the ship can't be targeted, is invisible on radar, and is ignored by AI unless firing."},
{ Mission::Parse_Object_Flags::SF_Friendly_stealth_invis, "If set and the ship is also flagged with stealth then the ship can't be targeted even by ships on the same team."},
{ Mission::Parse_Object_Flags::SF_Dont_collide_invis, "Will cause polygons with an invisible texture to stop colliding with objects."},
{ Mission::Parse_Object_Flags::SF_Primitive_sensors, "Targets will only be a blip on the radar. Ships cannot targeted and aspect lock cannot be used."},
{ Mission::Parse_Object_Flags::SF_No_subspace_drive, "Will not allow a ship to jump into subspace."},
{ Mission::Parse_Object_Flags::SF_Nav_carry_status, "This ship autopilots with the player."},
{ Mission::Parse_Object_Flags::SF_Affected_by_gravity, "Deprecated. Does nothing."},
{ Mission::Parse_Object_Flags::SF_Toggle_subsystem_scanning, "Switches between being able to scan a whole ship or individual subsystems."},
{ Mission::Parse_Object_Flags::OF_Targetable_as_bomb, "Allows ship to be targeted with the bomb targeting key."},
{ Mission::Parse_Object_Flags::SF_No_builtin_messages, "Ship will not send built-in messages."},
{ Mission::Parse_Object_Flags::SF_Primaries_locked, "Will stop a ship from firing their primary weapons."},
{ Mission::Parse_Object_Flags::SF_Secondaries_locked, "Will stop a ship from firing their secondary weapons."},
{ Mission::Parse_Object_Flags::SF_No_death_scream, "Ship will not send a death message."},
{ Mission::Parse_Object_Flags::SF_Always_death_scream, "Ship will always send a death message."},
{ Mission::Parse_Object_Flags::SF_Nav_needslink, "This ship requires \"linking\" for autopilot ."},
{ Mission::Parse_Object_Flags::SF_Hide_ship_name, "If set, the ship name can't be seen when the ship is targeted."},
{ Mission::Parse_Object_Flags::SF_Set_class_dynamically, "This ship should have its class assigned rather than simply read from the mission file."},
{ Mission::Parse_Object_Flags::SF_Lock_all_turrets_initially, "Lock all turrets on this ship at mission start or on arrival."},
{ Mission::Parse_Object_Flags::SF_Afterburner_locked, "Will stop a ship from firing their afterburner."},
{ Mission::Parse_Object_Flags::OF_Force_shields_on, "Shields will be activated regardless of other flags."},
{ Mission::Parse_Object_Flags::OF_Immobile, "Will not let a ship change position or orientation. Upon destruction the ship will still do the death roll and explosion."},
{ Mission::Parse_Object_Flags::OF_Dont_change_position, "Will not let a ship change position. Upon destruction the ship will still do the death roll and explosion."},
{ Mission::Parse_Object_Flags::OF_Dont_change_orientation, "Will not let a ship change orientation. Upon destruction the ship will still do the death roll and explosion."},
{ Mission::Parse_Object_Flags::SF_No_ets, "Will not allow a ship to alter its ETS system."},
{ Mission::Parse_Object_Flags::SF_Cloaked, "This ship will not be rendered."},
{ Mission::Parse_Object_Flags::SF_Ship_locked, "Prevents the player from changing the ship class on loadout screen."},
{ Mission::Parse_Object_Flags::SF_Weapons_locked, "Prevents the player from changing the weapons on the ship on the loadout screen."},
{ Mission::Parse_Object_Flags::SF_Scramble_messages, "All messages sent from this ship appear scrambled."},
{ Mission::Parse_Object_Flags::OF_No_collide, "Ship cannot be collided with."},
{ Mission::Parse_Object_Flags::SF_No_disabled_self_destruct, "Ship will not self-destruct after 90 seconds if engines or weapons destroyed."},
{ Mission::Parse_Object_Flags::SF_Hide_mission_log, "Mission log events generated for this ship will not be viewable."},
{ Mission::Parse_Object_Flags::SF_Same_arrival_warp_when_docked,"Docked ships use the same warp effect size upon arrival as if they were not docked instead of the enlarged aggregate size."},
{ Mission::Parse_Object_Flags::SF_Same_departure_warp_when_docked,"Docked ship use the same warp effect size upon departure as if they were not docked instead of the enlarged aggregate size."},
{ Mission::Parse_Object_Flags::OF_Attackable_if_no_collide, "Allows the AI to attack this object, even if no-collide is set."},
{ Mission::Parse_Object_Flags::SF_Fail_sound_locked_primary, "Play the firing fail sound when the weapon is locked."},
{ Mission::Parse_Object_Flags::SF_Fail_sound_locked_secondary, "Play the firing fail sound when the weapon is locked."},
{ Mission::Parse_Object_Flags::SF_Aspect_immune, "Ship cannot be targeted by Aspect Seekers."},
{ Mission::Parse_Object_Flags::SF_Cannot_perform_scan_hide_cargo, "Ship cannot scan other ships, and the cargo line will not be shown on the HUD."},
{ Mission::Parse_Object_Flags::SF_Cannot_perform_scan_show_cargo, "Ship cannot scan other ships, but the cargo line will be shown on the HUD."},
{ Mission::Parse_Object_Flags::SF_No_targeting_limits, "Ship is always targetable regardless of AWACS or targeting range limits."},
{ Mission::Parse_Object_Flags::SF_No_scanned_cargo, "Cargo is never revealed; only shows 'Scanned' or 'Not Scanned'. Needs $Unify Scanning Behavior in game_settings.tbl."},
};
const size_t Num_parse_object_flags = sizeof(Parse_object_flags) / sizeof(flag_def_list_new<Mission::Parse_Object_Flags>);
flag_def_list_new<Ship::Wing_Flags> Parse_wing_flags[] = {
{"ignore-count", Ship::Wing_Flags::Ignore_count, true, false},
{"reinforcement", Ship::Wing_Flags::Reinforcement, true, false},
{"no-arrival-music", Ship::Wing_Flags::No_arrival_music, true, false},
{"no-arrival-message", Ship::Wing_Flags::No_arrival_message, true, false},
{"no-first-wave-message", Ship::Wing_Flags::No_first_wave_message, true, false},
{"no-arrival-warp", Ship::Wing_Flags::No_arrival_warp, true, false},
{"no-departure-warp", Ship::Wing_Flags::No_departure_warp, true, false},
{"no-dynamic", Ship::Wing_Flags::No_dynamic, true, false},
{"nav-carry-status", Ship::Wing_Flags::Nav_carry, true, false},
{"same-arrival-warp-when-docked", Ship::Wing_Flags::Same_arrival_warp_when_docked, true, false},
{"same-departure-warp-when-docked", Ship::Wing_Flags::Same_departure_warp_when_docked, true, false}
};
parse_object_flag_description<Ship::Wing_Flags> Parse_wing_flag_descriptions[] = {
{ Ship::Wing_Flags::Ignore_count, "Ignore this wing when counting ship types for goals." },
{ Ship::Wing_Flags::Reinforcement, "This wing is a reinforcement wing." },
{ Ship::Wing_Flags::No_arrival_music, "Don't play arrival music when wing arrives." },
{ Ship::Wing_Flags::No_arrival_message, "Don't play arrival message when wing arrives." },
{ Ship::Wing_Flags::No_first_wave_message, "Don't play the 'first wave' message when this is the first wing to arrive." },
{ Ship::Wing_Flags::No_arrival_warp, "No arrival warp-in effect." },
{ Ship::Wing_Flags::No_departure_warp, "No departure warp-in effect." },
{ Ship::Wing_Flags::No_dynamic, "Will stop allowing the AI to pursue dynamic goals (eg: chasing ships it was not ordered to)." },
{ Ship::Wing_Flags::Nav_carry, "Ships in this wing autopilot with the player." },
{ Ship::Wing_Flags::Same_arrival_warp_when_docked, "Docked ships use the same warp effect size upon arrival as if they were not docked instead of the enlarged aggregate size." },
{ Ship::Wing_Flags::Same_departure_warp_when_docked, "Docked ship use the same warp effect size upon departure as if they were not docked instead of the enlarged aggregate size." }};
const size_t Num_parse_wing_flags = sizeof(Parse_wing_flags) / sizeof(flag_def_list_new<Ship::Wing_Flags>);
const size_t Num_parse_wing_flag_descriptions = sizeof(Parse_wing_flag_descriptions) / sizeof(parse_object_flag_description<Ship::Wing_Flags>);
flag_def_list_new<Mission::Parse_Object_Flags> Parse_prop_flags[] = {
{ "no_collide", Mission::Parse_Object_Flags::OF_No_collide, true, false },
};
parse_object_flag_description<Mission::Parse_Object_Flags> Parse_prop_flag_descriptions[] = {
{ Mission::Parse_Object_Flags::OF_No_collide, "Prop cannot be collided with."},
};
const size_t Num_parse_prop_flags = sizeof(Parse_prop_flags) / sizeof(flag_def_list_new<Mission::Parse_Object_Flags>);
const size_t Num_parse_prop_flag_descriptions = sizeof(Parse_prop_flag_descriptions) / sizeof(parse_object_flag_description<Mission::Parse_Object_Flags>);
// These are only the flags that are saved to the mission file. See the MEF_ #defines.
flag_def_list Mission_event_flags[] = {
{ "interval & delay use msecs", MEF_USE_MSECS, 0 },
};
int Num_mission_event_flags = sizeof(Mission_event_flags) / sizeof(flag_def_list);
const char *Mission_event_log_flags[MAX_MISSION_EVENT_LOG_FLAGS] = {
"true",
"false",
"always true", // disabled
"always false",
"first repeat",
"last repeat",
"first trigger",
"last trigger",
"state change",
};
//XSTR:ON
int Num_reinforcement_type_names = sizeof(Reinforcement_type_names) / sizeof(char *);
vec3d Parse_viewer_pos;
matrix Parse_viewer_orient;
int Loading_screen_bm_index=-1;
fix Mission_end_time;
// calculates a "unique" file signature as a ushort (checksum) and an int (file length)
// the amount of The_mission we're going to checksum
// WARNING : do NOT call this function on the server - it will overwrite goals, etc
#define MISSION_CHECKSUM_SIZE (NAME_LENGTH + NAME_LENGTH + 4 + DATE_TIME_LENGTH + DATE_TIME_LENGTH)
// a timer used to limit arrival music
#define ARRIVAL_MUSIC_MIN_SEPARATION 60000
// a random delay before announcing enemy arrivals
#define ARRIVAL_MESSAGE_DELAY_MIN 2000
#define ARRIVAL_MESSAGE_DELAY_MAX 3000
static int Allow_arrival_music_timestamp;
static int Allow_arrival_message_timestamp;
static int Arrival_message_delay_timestamp;
static int Arrival_message_subject;
static int Allow_backup_message_timestamp;
// multi TvT
static int Allow_arrival_music_timestamp_m[2];
static int Allow_arrival_message_timestamp_m[2];
static int Arrival_message_delay_timestamp_m[2];
// local prototypes
void parse_player_info2(mission *pm);
bool post_process_mission(mission *pm);
int allocate_subsys_status();
void parse_common_object_data(p_object *objp);
void parse_asteroid_fields(mission *pm);
int mission_set_arrival_location(anchor_t anchor, ArrivalLocation location, int distance, int objnum, int path_mask, vec3d *new_pos, matrix *new_orient);
anchor_t get_anchor(const char *name);
void mission_parse_set_up_initial_docks();
void mission_parse_set_arrival_locations();
void mission_set_wing_arrival_location( wing *wingp, int num_to_set );
void parse_object_set_handled_flag_helper(p_object *pobjp, p_dock_function_info *infop);
void parse_object_clear_all_handled_flags();
int parse_object_on_arrival_list(p_object *pobjp);
int add_path_restriction();
static bool Warned_about_team_out_of_range;
// Goober5000
void mission_parse_mark_non_arrival(p_object *p_objp);
void mission_parse_mark_non_arrival(wing *wingp);
void mission_parse_mark_non_arrivals();
// Goober5000 - FRED import
void convertFSMtoFS2();
MONITOR(NumShipArrivals)
MONITOR(NumShipDepartures)
const std::shared_ptr<scripting::Hook<scripting::hooks::ShipDepartConditions>> OnDepartureStartedHook = scripting::Hook<scripting::hooks::ShipDepartConditions>::Factory(
"On Departure Started", "Called when a ship initiates a departure (acquires a hangar bay path or begins to warp) but is not actually exiting the mission yet.",
{
{"Self", "ship", "An alias for Ship."},
{"Ship", "ship", "The ship that has begun the departure process."},
});
const std::shared_ptr<scripting::Hook<>> OnLoadoutAboutToParseHook = scripting::Hook<>::Factory("On Loadout About To Parse",
"Called during mission load just before parsing the team loadout.",{});
custom_string* get_custom_string_by_name(SCP_string name)
{
for (size_t i = 0; i < The_mission.custom_strings.size(); i++) {
if (The_mission.custom_strings[i].name == name) {
return &The_mission.custom_strings[i];
}
}
return nullptr;
}
// Goober5000
void parse_custom_bitmap(const char *expected_string_640, const char *expected_string_1024, char *string_field_640, char *string_field_1024)
{
bool found640 = false, found1024 = false;
// custom mission loading background, or whatever
if (optional_string(expected_string_640))
{
found640 = true;
stuff_string(string_field_640, F_NAME, MAX_FILENAME_LEN);
}
if (optional_string(expected_string_1024))
{
found1024 = true;
stuff_string(string_field_1024, F_NAME, MAX_FILENAME_LEN);
}
// error testing
if (Fred_running && (found640) && !(found1024))
{
mprintf(("Mission: found an entry for %s but not a corresponding entry for %s!\n", expected_string_640, expected_string_1024));
}
if (Fred_running && !(found640) && (found1024))
{
mprintf(("Mission: found an entry for %s but not a corresponding entry for %s!\n", expected_string_1024, expected_string_640));
}
}
void parse_mission_info(mission *pm, bool basic = false)
{
char game_string[NAME_LENGTH];
// Goober5000
skip_to_start_of_string("#Mission Info");
required_string("#Mission Info");
required_string("$Version:");
pm->required_fso_version = gameversion::parse_version_inline();
if (!gameversion::check_at_least(pm->required_fso_version))
throw parse::VersionException("Mission requires version " + gameversion::format_version(pm->required_fso_version), pm->required_fso_version);
required_string("$Name:");
stuff_string(pm->name, F_NAME);
required_string("$Author:");
stuff_string(pm->author, F_NAME);
required_string("$Created:");
stuff_string(pm->created, F_DATE, DATE_TIME_LENGTH);
required_string("$Modified:");
stuff_string(pm->modified, F_DATE, DATE_TIME_LENGTH);
required_string("$Notes:");
stuff_string(pm->notes, F_NOTES, NOTES_LENGTH);
if (optional_string("$Mission Desc:"))
stuff_string(pm->mission_desc, F_MULTITEXT, MISSION_DESC_LENGTH);
if ( optional_string("+Game Type:")) {
// HACK HACK HACK -- stuff_string was changed to *not* ignore carriage returns. Since the
// old style missions may have carriage returns, deal with it here.
ignore_white_space();
stuff_string(game_string, F_NAME, NAME_LENGTH);
for ( int i = 0; i < OLD_MAX_GAME_TYPES; i++ ) {
if ( !stricmp(game_string, Old_game_types[i]) ) {
// this block of code is now old mission compatibility code. We specify game
// type in a different manner than before.
if ( i == OLD_GAME_TYPE_SINGLE_ONLY )
pm->game_type = MISSION_TYPE_SINGLE;
else if ( i == OLD_GAME_TYPE_MULTI_ONLY )
pm->game_type = MISSION_TYPE_MULTI;
else if ( i == OLD_GAME_TYPE_SINGLE_MULTI )
pm->game_type = (MISSION_TYPE_SINGLE | MISSION_TYPE_MULTI );
else if ( i == OLD_GAME_TYPE_TRAINING )
pm->game_type = MISSION_TYPE_TRAINING;
else
Int3();
if ( pm->game_type & MISSION_TYPE_MULTI )
pm->game_type |= MISSION_TYPE_MULTI_COOP;
break;
}
}
}
if ( optional_string("+Game Type Flags:") ) {
stuff_int(&pm->game_type);
}
// multiplayer team v. team games have two teams. If we have three teams, we need to use
// a new mission mode!
if ( (pm->game_type & MISSION_TYPE_MULTI) && (pm->game_type & MISSION_TYPE_MULTI_TEAMS) ){
Num_teams = 2;
}
if (optional_string("+Flags:")){
stuff_flagset(&pm->flags);
}
// nebula mission stuff
if(optional_string("+NebAwacs:")){
stuff_float(&Neb2_awacs);
}
if(optional_string("+Storm:")){
stuff_string(Mission_parse_storm_name, F_NAME, NAME_LENGTH);
if (!basic)
nebl_set_storm(Mission_parse_storm_name);
}
bool found_neb2_modern = false;
if (optional_string("+Fog Near Mult:")) {
stuff_float(&Neb2_fog_legacy_near_mult);
}
if (optional_string("+Fog Far Mult:")) {
stuff_float(&Neb2_fog_legacy_far_mult);
}
// Look for the modern values
if (optional_string("+Fog 1000m Visibility:")) {
stuff_float(&Neb2_fog_1000m_visibility);
found_neb2_modern = true;
}
if (optional_string("+Fog Near Distance:")) {
stuff_float(&Neb2_fog_near_distance);
found_neb2_modern = true;
}
if (optional_string("+Fog Skybox Clip Distance:")) {
stuff_float(&Neb2_fog_skybox_clip_distance);
found_neb2_modern = true;
}
if (optional_string("+Fog Clip Distance:")) {
stuff_float(&Neb2_fog_clip_distance);
found_neb2_modern = true;
}
// write legacy values on save if we are compatible with older formats
Neb2_fog_save_legacy_values = !found_neb2_modern;
// Convert legacy values if this mission was made before modern values were introduced
if (!found_neb2_modern) {
//This stems from the weird unchangeable constants of legacy fog
float denom = std::max(75.f * Neb2_fog_legacy_far_mult - Neb2_fog_legacy_near_mult, 1.0f);
Neb2_fog_1000m_visibility = powf(10.f, -100.f / denom);
Neb2_fog_near_distance = 10.f * Neb2_fog_legacy_near_mult;
Neb2_fog_skybox_clip_distance = 0.f; // Apparently, skybox fog was just outright broken...
Neb2_fog_clip_distance = Default_max_draw_distance;
}
// Goober5000 - ship contrail speed threshold
if (optional_string("$Contrail Speed Threshold:")){
stuff_int(&pm->contrail_threshold);
}
if (optional_string("+Large Ship Collision Group:")) {
stuff_int(&pm->large_ship_no_collide_collision_group);
if (pm->large_ship_no_collide_collision_group < 0 || pm->large_ship_no_collide_collision_group > 31) {
WarningEx(LOCATION,
"Invalid large ship collision group id %d specified. Valid IDs range from 0 to 31. Using group %d instead.\n",
pm->large_ship_no_collide_collision_group,
DEFAULT_LARGE_SHIP_NO_COLLIDE_COLLISION_GROUP);
pm->large_ship_no_collide_collision_group = DEFAULT_LARGE_SHIP_NO_COLLIDE_COLLISION_GROUP;
}
}
if (optional_string("+Volumetric Nebula:")) {
pm->volumetrics.emplace().parse_volumetric_nebula();
}
// get the number of players if in a multiplayer mission
if ( pm->game_type & MISSION_TYPE_MULTI ) {
if ( optional_string("+Num Players:") ) {
stuff_int( &(pm->num_players) );
}
}
// get the number of respawns
if ( pm->game_type & MISSION_TYPE_MULTI ) {
if ( optional_string("+Num Respawns:") ){
stuff_int( (int*)&(pm->num_respawns) );
}
}
if ( pm->game_type & MISSION_TYPE_MULTI ) {
if ( optional_string("+Max Respawn Time:") ){
stuff_int( &The_mission.max_respawn_delay );
}
}
if ( optional_string("+Red Alert:")) {
int temp;
stuff_int(&temp);
pm->flags.set(Mission::Mission_Flags::Red_alert, temp != 0);
}
if ( optional_string("+Scramble:")) {
int temp;
stuff_int(&temp);
pm->flags.set(Mission::Mission_Flags::Scramble, temp != 0);
}
// if we are just requesting basic info then skip everything else. the reason
// for this is to make sure that we don't modify things outside of the mission struct
// that might not get reset afterwards (like what can happen in the techroom) - taylor
//
// NOTE: this can be dangerous so be sure that any get_mission_info() call (defaults to basic info) will
// only reference data parsed before this point!! (like current FRED2 and game code does)
if (basic)
return;
if ( optional_string("+Disallow Support:"))
{
int temp;
stuff_int(&temp);
pm->support_ships.max_support_ships = (temp > 0) ? 0 : -1;
}
if (optional_string("+Disallow Support Rearm:")) {
int temp;
stuff_int(&temp);
pm->support_ships.disallow_rearm = (temp != 0);
}
if (optional_string("+Allow Support Rearm Weapon Precedence:")) {
int temp;
stuff_int(&temp);
pm->support_ships.allow_rearm_weapon_precedence = (temp != 0);
}
if ( optional_string("+Hull Repair Ceiling:"))
{
float temp;
stuff_float(&temp);
//ONLY set max_hull_repair_val if the value given is valid -C
if (temp <= 100.0f && temp >= 0.0f) {
pm->support_ships.max_hull_repair_val = temp;
}
}
if ( optional_string("+Subsystem Repair Ceiling:"))
{
float temp;