]> icculus.org git repositories - taylor/freespace2.git/blob - src/missionui/redalert.cpp
makefile: disable stand_gui for now.
[taylor/freespace2.git] / src / missionui / redalert.cpp
1 /*
2  * $Logfile: /Freespace2/code/MissionUI/RedAlert.cpp $
3  * $Revision$
4  * $Date$
5  * $Author$
6  *
7  * Module for Red Alert mission interface and code
8  *
9  * $Log$
10  * Revision 1.3  2002/05/26 22:06:17  relnev
11  * makefile: disable stand_gui for now.
12  *
13  * rest: staticize some globals
14  *
15  * Revision 1.2  2002/05/03 13:34:33  theoddone33
16  * More stuff compiles
17  *
18  * Revision 1.1.1.1  2002/05/03 03:28:10  root
19  * Initial import.
20  *
21  * 
22  * 16    9/11/99 12:31a Mikek
23  * Bumped up max red alert status ships and status subsystems.
24  * 
25  * 15    9/06/99 6:38p Dave
26  * Improved CD detection code.
27  * 
28  * 14    8/24/99 5:27p Andsager
29  * Make subsystems with zero strength before mission blown off.  Protect
30  * red alert pilot against dying between orders and jump.
31  * 
32  * 13    8/11/99 2:17p Jefff
33  * changed a string
34  * 
35  * 12    7/19/99 8:56p Andsager
36  * Added Ship_exited red_alert_carry flag and hull strength for RED ALERT
37  * carry over of Exited_ships
38  * 
39  * 11    7/19/99 2:13p Dave
40  * Added some new strings for Heiko.
41  * 
42  * 10    7/16/99 12:22p Jefff
43  * Added sound FX to red alert popup
44  * 
45  * 9     7/09/99 10:32p Dave
46  * Command brief and red alert screens.
47  * 
48  * 8     5/07/99 10:34a Andsager
49  * Make red alert work in FS2
50  * 
51  * 7     2/16/99 5:07p Neilk
52  * Added hires support
53  * 
54  * 6     1/30/99 5:08p Dave
55  * More new hi-res stuff.Support for nice D3D textures.
56  * 
57  * 5     1/27/99 9:56a Dave
58  * Temporary checkin of beam weapons for Dan to make cool sounds.
59  * 
60  * 4     11/18/98 11:15a Andsager
61  * Removed old version or red_alert_read_wingman_status
62  * 
63  * 3     10/13/98 9:29a Dave
64  * Started neatening up freespace.h. Many variables renamed and
65  * reorganized. Added AlphaColors.[h,cpp]
66  * 
67  * 2     10/07/98 10:53a Dave
68  * Initial checkin.
69  * 
70  * 1     10/07/98 10:50a Dave
71  * 
72  * 21    5/14/98 11:26a Lawrance
73  * ESC will return to the main hall
74  * 
75  * 20    5/13/98 5:14p Allender
76  * red alert support to go back to previous mission
77  * 
78  * 19    5/05/98 6:19p Lawrance
79  * Fix problems with "retry mission" for red alerts
80  * 
81  * 18    5/05/98 3:14p Comet
82  * AL: Fix bug with restoring secondary weapons in red alert mode
83  * 
84  * 17    5/04/98 10:49p Lawrance
85  * Make sure old pilot files don't cause problems with the new RedAlert
86  * data format
87  * 
88  * 16    5/04/98 6:06p Lawrance
89  * Make red alert mode work!
90  * 
91  * 15    5/02/98 4:10p Lawrance
92  * Only use first stage of briefing for red alerts
93  * 
94  * 14    5/01/98 9:18p Ed
95  * mark all goals and events as failed when red alert moves ahead to next
96  * mission
97  * 
98  * 13    4/22/98 10:13a John
99  * added assert to trap a bug
100  * 
101  * 12    4/21/98 12:08a Allender
102  * only make red alert move to next mission in campaign mode
103  * 
104  * 11    4/03/98 10:31a John
105  * Made briefing and debriefing arrays be malloc'd
106  * 
107  * 10    3/28/98 2:53p Allender
108  * added hud gauge when entering a red alert mission
109  * 
110  * 9     3/12/98 10:28p Lawrance
111  * Deal with situation where wingman status may not have been properly
112  * saved
113  * 
114  * 8     3/12/98 9:44p Allender
115  * go to debrief in red alert when not in campaign mode
116  * 
117  * 6     3/09/98 9:55p Lawrance
118  * improve some comments
119  * 
120  * 5     3/09/98 4:41p Lawrance
121  * Fix up some merge problems.
122  * 
123  * 4     3/09/98 4:30p Allender
124  * multiplayer secondary weapon changes.  red-alert and cargo-known-delay
125  * sexpressions.  Add time cargo revealed to ship structure
126  * 
127  * 3     3/09/98 4:23p Lawrance
128  * Replay mission, full save/restore of wingman status
129  * 
130  * 2     3/09/98 12:13a Lawrance
131  * Add support for Red Alert missions
132  * 
133  * 1     3/08/98 4:54p Lawrance
134  *
135  * $NoKeywords: $
136  */
137
138 #include "ui.h"
139 #include "gamesnd.h"
140 #include "gamesequence.h"
141 #include "missionscreencommon.h"
142 #include "key.h"
143 #include "bmpman.h"
144 #include "font.h"
145 #include "redalert.h"
146 #include "hud.h"
147 #include "missionbriefcommon.h"
148 #include "timer.h"
149 #include "missioncampaign.h"
150 #include "missiongoals.h"
151 #include "linklist.h"
152 #include "hudwingmanstatus.h"
153 #include "audiostr.h"
154 #include "freespace.h"
155 #include "alphacolors.h"
156
157 /////////////////////////////////////////////////////////////////////////////
158 // Red Alert Mission-Level
159 /////////////////////////////////////////////////////////////////////////////
160
161 static int Red_alert_status;
162 static int Red_alert_new_mission_timestamp;             // timestamp used to give user a little warning for red alerts
163 static int Red_alert_num_slots_used = 0;
164 static int Red_alert_voice_started;
165
166 #define RED_ALERT_WARN_TIME             4000                            // time to warn user that new orders are coming
167
168 #define RED_ALERT_NONE                          0
169 #define RED_ALERT_MISSION                       1
170
171 #define MAX_RED_ALERT_SLOTS                             32
172 #define MAX_RED_ALERT_SUBSYSTEMS                64
173 #define RED_ALERT_EXITED_SHIP_CLASS             -1
174
175 typedef struct red_alert_ship_status
176 {
177         char    name[NAME_LENGTH];
178         float   hull;
179         int     ship_class;
180         float   subsys_current_hits[MAX_RED_ALERT_SUBSYSTEMS];
181         float subsys_aggregate_current_hits[SUBSYSTEM_MAX];
182         int     wep[MAX_WL_WEAPONS];
183         int     wep_count[MAX_WL_WEAPONS];
184 } red_alert_ship_status;
185
186 static red_alert_ship_status Red_alert_wingman_status[MAX_RED_ALERT_SLOTS];
187 static char     Red_alert_precursor_mission[MAX_FILENAME_LEN];
188
189 /////////////////////////////////////////////////////////////////////////////
190 // Red Alert Interface
191 /////////////////////////////////////////////////////////////////////////////
192
193 char *Red_alert_fname[GR_NUM_RESOLUTIONS] = {
194         "RedAlert",
195         "2_RedAlert"
196 };
197
198 char *Red_alert_mask[GR_NUM_RESOLUTIONS] = {
199         "RedAlert-m",
200         "2_RedAlert-m"
201 };
202
203 // font to use for "incoming transmission"
204 int Ra_flash_font[GR_NUM_RESOLUTIONS] = {
205         FONT2,
206         FONT2
207 };
208
209 int Ra_flash_y[GR_NUM_RESOLUTIONS] = {
210         140,
211         200
212 };
213
214 static int Ra_flash_coords[GR_NUM_RESOLUTIONS][2] = {
215         {
216                 61, 108                 // GR_640
217         },
218         {
219                 61, 108                 // GR_1024
220         }
221 };
222
223 #define NUM_BUTTONS                                             2
224
225 #define RA_REPLAY_MISSION                               0
226 #define RA_CONTINUE                                             1
227
228 static ui_button_info Buttons[GR_NUM_RESOLUTIONS][NUM_BUTTONS] = {
229         {       // GR_640
230                 ui_button_info("RAB_00",        2,              445,    -1,     -1, 0),
231                 ui_button_info("RAB_01",        575,    432,    -1,     -1, 1),
232         },      
233         {       // GR_1024
234                 ui_button_info("2_RAB_00",      4,              712,    -1,     -1, 0),
235                 ui_button_info("2_RAB_01",      920,    691,    -1,     -1, 1),
236         }
237 };
238
239 #define RED_ALERT_NUM_TEXT              3
240 UI_XSTR Red_alert_text[GR_NUM_RESOLUTIONS][RED_ALERT_NUM_TEXT] = {
241         { // GR_640
242                 { "Replay",             1405,   46,     451,    UI_XSTR_COLOR_PINK,     -1, &Buttons[0][RA_REPLAY_MISSION].button },
243                 { "Previous Mission",   1452,   46,     462,    UI_XSTR_COLOR_PINK,     -1, &Buttons[0][RA_REPLAY_MISSION].button },
244                 { "Continue",   1069,   564,    413,    UI_XSTR_COLOR_PINK,     -1, &Buttons[0][RA_CONTINUE].button },
245         },
246         { // GR_1024
247                 { "Replay",             1405,   75,     722,    UI_XSTR_COLOR_PINK,     -1, &Buttons[1][RA_REPLAY_MISSION].button },
248                 { "Previous Mission",   1452,   75,     733,    UI_XSTR_COLOR_PINK,     -1, &Buttons[1][RA_REPLAY_MISSION].button },
249                 { "Continue",   1069,   902,    661,    UI_XSTR_COLOR_PINK,     -1, &Buttons[1][RA_CONTINUE].button },
250         }
251 };
252
253 // indicies for coordinates
254 #define RA_X_COORD 0
255 #define RA_Y_COORD 1
256 #define RA_W_COORD 2
257 #define RA_H_COORD 3
258
259
260 static int Text_delay;
261
262 int Ra_brief_text_wnd_coords[GR_NUM_RESOLUTIONS][4] = {
263         {
264                 14, 151, 522, 289
265         },
266         {
267                 52, 241, 785, 463
268         }
269 };
270
271 static UI_WINDOW Ui_window;
272 // static hud_anim Flash_anim;
273 static int Background_bitmap;
274 static int Red_alert_inited = 0;
275
276 static int Red_alert_voice;
277
278 // open and pre-load the stream buffers for the different voice streams
279 void red_alert_voice_load()
280 {
281         Assert( Briefing != NULL );
282         if ( strnicmp(Briefing->stages[0].voice, NOX("none"), 4) && (strlen(Briefing->stages[0].voice) > 0) ) {
283                 Red_alert_voice = audiostream_open( Briefing->stages[0].voice, ASF_VOICE );
284         }
285 }
286
287 // close all the briefing voice streams
288 void red_alert_voice_unload()
289 {
290         if ( Red_alert_voice != -1 ) {
291                 audiostream_close_file(Red_alert_voice, 0);
292                 Red_alert_voice = -1;
293         }
294 }
295
296 // start playback of the red alert voice
297 void red_alert_voice_play()
298 {
299         if ( Red_alert_voice == -1 ){
300                 return; // voice file doesn't exist
301         }
302
303         if ( !Briefing_voice_enabled ) {
304                 return;
305         }
306
307         if ( audiostream_is_playing(Red_alert_voice) ){
308                 return;
309         }
310
311         audiostream_play(Red_alert_voice, Master_voice_volume, 0);
312         Red_alert_voice_started = 1;
313 }
314
315 // stop playback of the red alert voice
316 void red_alert_voice_stop()
317 {
318         if ( Red_alert_voice == -1 )
319                 return;
320
321         audiostream_stop(Red_alert_voice);      // stream is automatically rewound
322 }
323
324 // a button was pressed, deal with it
325 void red_alert_button_pressed(int n)
326 {
327         switch (n) {
328         case RA_CONTINUE:               
329                 if(game_do_cd_mission_check(Game_current_mission_filename)){
330                         gameseq_post_event(GS_EVENT_ENTER_GAME);
331                 } else {
332                         gameseq_post_event(GS_EVENT_MAIN_MENU);
333                 }
334                 break;
335
336         case RA_REPLAY_MISSION:
337                 if ( Game_mode & GM_CAMPAIGN_MODE ) {
338                         // TODO: make call to campaign code to set correct mission for loading
339                         // mission_campaign_play_previous_mission(Red_alert_precursor_mission);
340                         if ( !mission_campaign_previous_mission() ) {
341                                 gamesnd_play_iface(SND_GENERAL_FAIL);
342                                 break;
343                         }
344
345                         // CD CHECK
346                         if(game_do_cd_mission_check(Game_current_mission_filename)){
347                                 gameseq_post_event(GS_EVENT_START_GAME);
348                         } else {
349                                 gameseq_post_event(GS_EVENT_MAIN_MENU);
350                         }
351                 } else {
352                         gamesnd_play_iface(SND_GENERAL_FAIL);
353                 }
354                 break;
355         }
356 }
357
358 // blit "incoming transmission"
359 #define RA_FLASH_CYCLE                  0.25f
360 float Ra_flash_time = 0.0f;
361 int Ra_flash_up = 0;
362 void red_alert_blit_title()
363 {
364         char *str = XSTR("Incoming Transmission", 1406);
365         int w, h;
366
367         // get the string size  
368         gr_set_font(Ra_flash_font[gr_screen.res]);
369         gr_get_string_size(&w, &h, str);
370
371         // set alpha color
372         color flash_color;
373         if(Ra_flash_up){
374                 gr_init_alphacolor(&flash_color, (int)(255.0f * (Ra_flash_time / RA_FLASH_CYCLE)), 0, 0, 255);
375         } else {
376                 gr_init_alphacolor(&flash_color, (int)(255.0f * (1.0f - (Ra_flash_time / RA_FLASH_CYCLE))), 0, 0, 255);
377         }
378
379         // draw
380         gr_set_color_fast(&flash_color);
381         gr_string(Ra_brief_text_wnd_coords[gr_screen.res][0] + ((Ra_brief_text_wnd_coords[gr_screen.res][2] - w) / 2), Ra_flash_y[gr_screen.res] - h - 5, str);
382         gr_set_color_fast(&Color_normal);       
383
384         // increment flash time
385         Ra_flash_time += flFrametime;
386         if(Ra_flash_time >= RA_FLASH_CYCLE){
387                 Ra_flash_time = 0.0f;
388                 Ra_flash_up = !Ra_flash_up;
389         }
390
391         // back to the original font
392         gr_set_font(FONT1);
393 }
394
395 // Called once when red alert interface is started
396 void red_alert_init()
397 {
398         int i;
399         ui_button_info *b;
400
401         if ( Red_alert_inited ) {
402                 return;
403         }
404
405         // common_set_interface_palette("ControlConfigPalette");  // set the interface palette
406         Ui_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0);
407         Ui_window.set_mask_bmap(Red_alert_mask[gr_screen.res]);
408
409         for (i=0; i<NUM_BUTTONS; i++) {
410                 b = &Buttons[gr_screen.res][i];
411
412                 b->button.create(&Ui_window, "", b->x, b->y, 60, 30, 0, 1);
413                 // set up callback for when a mouse first goes over a button
414                 b->button.set_highlight_action(common_play_highlight_sound);
415                 b->button.set_bmaps(b->filename);
416                 b->button.link_hotspot(b->hotspot);
417         }
418
419         // all text
420         for(i=0; i<RED_ALERT_NUM_TEXT; i++){
421                 Ui_window.add_XSTR(&Red_alert_text[gr_screen.res][i]);
422         }
423
424         // set up red alert hotkeys
425         Buttons[gr_screen.res][RA_CONTINUE].button.set_hotkey(KEY_CTRLED | KEY_ENTER);
426
427         // load in background image and flashing red alert animation
428         Background_bitmap = bm_load(Red_alert_fname[gr_screen.res]);
429         
430         // hud_anim_init(&Flash_anim, Ra_flash_coords[gr_screen.res][RA_X_COORD], Ra_flash_coords[gr_screen.res][RA_Y_COORD], NOX("AlertFlash"));
431         // hud_anim_load(&Flash_anim);
432
433         Red_alert_voice = -1;
434
435         if ( !Briefing ) {
436                 Briefing = &Briefings[0];                       
437         }
438
439         if ( Briefing->num_stages > 0 ) {
440                 Assert(Briefing->stages[0].new_text);
441                 brief_color_text_init(Briefing->stages[0].new_text, Ra_brief_text_wnd_coords[gr_screen.res][RA_W_COORD], 0);
442         }
443
444         red_alert_voice_load();
445
446         Text_delay = timestamp(200);
447
448         Red_alert_voice_started = 0;
449         Red_alert_inited = 1;
450 }
451
452 // Called once when the red alert interface is exited
453 void red_alert_close()
454 {
455         if (Red_alert_inited) {
456
457                 red_alert_voice_stop();
458                 red_alert_voice_unload();
459
460                 if (Background_bitmap >= 0) {
461                         bm_unload(Background_bitmap);
462                 }
463                 
464                 Ui_window.destroy();
465                 // hud_anim_release(&Flash_anim);
466                 common_free_interface_palette();                // restore game palette
467                 game_flush();
468         }
469
470         Red_alert_inited = 0;
471 }
472
473 // called once per frame when game state is GS_STATE_RED_ALERT
474 void red_alert_do_frame(float frametime)
475 {
476         int i, k;       
477
478         // ensure that the red alert interface has been initialized
479         if (!Red_alert_inited) {
480                 Int3();
481                 return;
482         }
483
484         k = Ui_window.process() & ~KEY_DEBUGGED;
485         switch (k) {
486                 case KEY_ESC:
487 //                      gameseq_post_event(GS_EVENT_ENTER_GAME);
488                         gameseq_post_event(GS_EVENT_MAIN_MENU);
489                         break;
490         }       // end switch
491
492         for (i=0; i<NUM_BUTTONS; i++){
493                 if (Buttons[gr_screen.res][i].button.pressed()){
494                         red_alert_button_pressed(i);
495                 }
496         }
497
498         GR_MAYBE_CLEAR_RES(Background_bitmap);
499         if (Background_bitmap >= 0) {
500                 gr_set_bitmap(Background_bitmap);
501                 gr_bitmap(0, 0);
502         } 
503
504         Ui_window.draw();
505         // hud_anim_render(&Flash_anim, frametime);
506
507         gr_set_font(FONT1);
508
509         if ( timestamp_elapsed(Text_delay) ) {
510                 int finished_wipe = 0;
511                 if ( Briefing->num_stages > 0 ) {
512                         finished_wipe = brief_render_text(0, Ra_brief_text_wnd_coords[gr_screen.res][RA_X_COORD], Ra_brief_text_wnd_coords[gr_screen.res][RA_Y_COORD], Ra_brief_text_wnd_coords[gr_screen.res][RA_H_COORD], frametime, 0);
513                 }
514
515                 if (finished_wipe) {
516                         red_alert_voice_play();
517                 }
518         }
519
520         // blit incoming transmission
521         red_alert_blit_title();
522
523         gr_flip();
524 }
525
526 // set the red alert status for the current mission
527 void red_alert_set_status(int status)
528 {
529         Red_alert_status = status;
530         Red_alert_new_mission_timestamp = timestamp(-1);                // make invalid
531 }
532
533 // Store a ships weapons into a wingman status structure
534 void red_alert_store_weapons(red_alert_ship_status *ras, ship_weapon *swp)
535 {
536         int i, sidx;
537
538         if (swp == NULL) {
539                 return;
540         }
541
542         for ( i = 0; i < MAX_WL_PRIMARY; i++ ) {
543                 ras->wep[i] = swp->primary_bank_weapons[i];
544                 if ( ras->wep[i] >= 0 ) {
545                         ras->wep_count[i] = 1;
546                 } else {
547                         ras->wep_count[i] = -1;
548                 }
549         }
550
551         for ( i = 0; i < MAX_WL_SECONDARY; i++ ) {
552                 sidx = i+MAX_WL_PRIMARY;
553                 ras->wep[sidx] = swp->secondary_bank_weapons[i];
554                 if ( ras->wep[sidx] >= 0 ) {
555                         ras->wep_count[sidx] = swp->secondary_bank_ammo[i];
556                 } else {
557                         ras->wep_count[sidx] = -1;
558                 }
559         }
560 }
561
562 // Take the weapons stored in a wingman_status struct, and bash them into the ship weapons struct
563 void red_alert_bash_weapons(red_alert_ship_status *ras, ship_weapon *swp)
564 {
565         int i, j, sidx;
566
567         // restore from ship_exited
568         if (ras->ship_class == RED_ALERT_EXITED_SHIP_CLASS) {
569                 return;
570         }
571
572         j = 0;
573         for ( i = 0; i < MAX_WL_PRIMARY; i++ ) {
574                 if ( (ras->wep_count[i] > 0) && (ras->wep[i] >= 0) ) {
575                         swp->primary_bank_weapons[j] = ras->wep[i];                     
576                         j++;
577                 }
578         }
579         swp->num_primary_banks = j;
580
581         j = 0;
582         for ( i = 0; i < MAX_WL_SECONDARY; i++ ) {
583                 sidx = i+MAX_WL_PRIMARY;
584                 if ( ras->wep[sidx] >= 0 ) {
585                         swp->secondary_bank_weapons[j] = ras->wep[sidx];
586                         swp->secondary_bank_ammo[j] = ras->wep_count[sidx];
587                         j++;
588                 }
589         }
590         swp->num_secondary_banks = j;
591 }
592
593 void red_alert_bash_subsys_status(red_alert_ship_status *ras, ship *shipp)
594 {
595         ship_subsys *ss;
596         int                     count = 0;
597
598         // restore from ship_exited
599         if (ras->ship_class == RED_ALERT_EXITED_SHIP_CLASS) {
600                 return;
601         }
602
603         ss = GET_FIRST(&shipp->subsys_list);
604         while ( ss != END_OF_LIST( &shipp->subsys_list ) ) {
605
606                 if ( count >= MAX_RED_ALERT_SUBSYSTEMS ) {
607                         Int3(); // ran out of subsystems
608                         break;
609                 }
610
611                 ss->current_hits = ras->subsys_current_hits[count];
612                 if (ss->current_hits <= 0) {
613                         ss->submodel_info_1.blown_off = 1;
614                 }
615
616                 ss = GET_NEXT( ss );
617                 count++;
618         }
619
620         int i;
621
622         for ( i = 0; i < SUBSYSTEM_MAX; i++ ) {
623                 shipp->subsys_info[i].current_hits = ras->subsys_aggregate_current_hits[i];
624         }
625 }
626
627
628 void red_alert_store_subsys_status(red_alert_ship_status *ras, ship *shipp)
629 {
630         ship_subsys *ss;
631         int                     count = 0;
632
633         if (shipp == NULL) {
634                 return;
635         }
636
637         ss = GET_FIRST(&shipp->subsys_list);
638         while ( ss != END_OF_LIST( &shipp->subsys_list ) ) {
639
640                 if ( count >= MAX_RED_ALERT_SUBSYSTEMS ) {
641                         Int3(); // ran out of subsystems
642                         break;
643                 }
644
645                 ras->subsys_current_hits[count] = ss->current_hits;
646
647                 ss = GET_NEXT( ss );
648                 count++;
649         }
650
651         int i;
652
653         for ( i = 0; i < SUBSYSTEM_MAX; i++ ) {
654                 ras->subsys_aggregate_current_hits[i] = shipp->subsys_info[i].current_hits;
655         }
656 }
657
658
659 // Record the current state of the players wingman
660 void red_alert_store_wingman_status()
661 {
662         ship                            *shipp;
663         red_alert_ship_status   *ras;
664         ship_obj                        *so;
665         object                  *ship_objp;
666
667         Red_alert_num_slots_used = 0;
668
669         // store the mission filename for the red alert precursor mission
670         strcpy(Red_alert_precursor_mission, Game_current_mission_filename);;
671
672         // store status for all existing ships
673         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
674                 ship_objp = &Objects[so->objnum];
675                 Assert(ship_objp->type == OBJ_SHIP);
676                 shipp = &Ships[ship_objp->instance];
677
678                 if ( shipp->flags & SF_DYING ) {
679                         continue;
680                 }
681
682                 if ( Red_alert_num_slots_used >= MAX_RED_ALERT_SLOTS ) {
683                         Int3(); // ran out of red alert slots
684                         continue;
685                 }
686
687                 if ( !(shipp->flags & SF_FROM_PLAYER_WING) && !(shipp->flags & SF_RED_ALERT_STORE_STATUS) ) {
688                         continue;
689                 }
690
691                 ras = &Red_alert_wingman_status[Red_alert_num_slots_used];
692                 Red_alert_num_slots_used++;
693
694                 strcpy(ras->name, shipp->ship_name);
695                 ras->hull = Objects[shipp->objnum].hull_strength;
696                 ras->ship_class = shipp->ship_info_index;
697                 red_alert_store_weapons(ras, &shipp->weapons);
698                 red_alert_store_subsys_status(ras, shipp);
699         }
700
701         // store exited ships that did not die
702         for (int idx=0; idx<MAX_EXITED_SHIPS; idx++) {
703
704                 if ( Red_alert_num_slots_used >= MAX_RED_ALERT_SLOTS ) {
705                         Int3(); // ran out of red alert slots
706                         continue;
707                 }
708
709                 if (Ships_exited[idx].flags & SEF_RED_ALERT_CARRY) {
710                         ras = &Red_alert_wingman_status[Red_alert_num_slots_used];
711                         Red_alert_num_slots_used++;
712
713                         strcpy(ras->name, Ships_exited[idx].ship_name);
714                         ras->hull = float(Ships_exited[idx].hull_strength);
715                         ras->ship_class = RED_ALERT_EXITED_SHIP_CLASS; //shipp->ship_info_index;
716                         red_alert_store_weapons(ras, NULL);
717                         red_alert_store_subsys_status(ras, NULL);
718                 }
719         }
720
721         Assert(Red_alert_num_slots_used > 0);
722 }
723
724 // Delete a ship in a red alert mission (since it must have died/departed in the previous mission)
725 void red_alert_delete_ship(ship *shipp)
726 {
727         if ( (shipp->wing_status_wing_index >= 0) && (shipp->wing_status_wing_pos >= 0) ) {
728                 hud_set_wingman_status_dead(shipp->wing_status_wing_index, shipp->wing_status_wing_pos);
729         }
730
731         ship_add_exited_ship( shipp, SEF_PLAYER_DELETED );
732         obj_delete(shipp->objnum);
733         if ( shipp->wingnum >= 0 ) {
734                 ship_wing_cleanup( shipp-Ships, &Wings[shipp->wingnum] );
735         }
736 }
737
738 // Take the stored wingman status information, and adjust the player wing ships accordingly
739 void red_alert_bash_wingman_status()
740 {
741         int                             i;
742         ship                            *shipp;
743         red_alert_ship_status   *ras;
744         ship_obj                        *so;
745         object                  *ship_objp;
746
747         if ( !(Game_mode & GM_CAMPAIGN_MODE) ) {
748                 return;
749         }
750
751         if ( Red_alert_num_slots_used <= 0 ) {
752                 return;
753         }
754
755         // go through all ships in the game, and see if there is red alert status data for any
756
757         int remove_list[MAX_RED_ALERT_SLOTS];
758         int remove_count = 0;
759
760         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
761                 ship_objp = &Objects[so->objnum];
762                 Assert(ship_objp->type == OBJ_SHIP);
763                 shipp = &Ships[ship_objp->instance];
764
765                 if ( !(shipp->flags & SF_FROM_PLAYER_WING) && !(shipp->flags & SF_RED_ALERT_STORE_STATUS) ) {
766                         continue;
767                 }
768
769                 int found_match = 0;
770
771                 for ( i = 0; i < Red_alert_num_slots_used; i++ ) {
772                         ras = &Red_alert_wingman_status[i];
773
774                         if ( !stricmp(ras->name, shipp->ship_name) ) {
775                                 found_match = 1;
776                                 if ( ras->ship_class == RED_ALERT_EXITED_SHIP_CLASS) {
777                                         // if exited ship, we can only restore hull strength
778                                         ship_objp->hull_strength = ras->hull;
779                                 } else {
780                                         // if necessary, restore correct ship class
781                                         if ( ras->ship_class != shipp->ship_info_index ) {
782                                                 change_ship_type(SHIP_INDEX(shipp), ras->ship_class);
783                                         }
784                                         // restore hull and weapons
785                                         ship_objp->hull_strength = ras->hull;
786                                         red_alert_bash_weapons(ras, &shipp->weapons);
787                                         red_alert_bash_subsys_status(ras, shipp);
788                                 }
789                         }
790                 }
791
792                 if ( !found_match ) {
793                         remove_list[remove_count++] = SHIP_INDEX(shipp);
794                 }
795         }
796
797         // remove ships
798         for ( i = 0; i < remove_count; i++ ) {
799                 // remove ship
800                 red_alert_delete_ship(&Ships[remove_list[i]]);
801         }
802 }
803
804 // write wingman status out to the specified file
805 void red_alert_write_wingman_status(CFILE *fp)
806 {
807         int                             i, j;
808         red_alert_ship_status *ras;
809
810         cfwrite_int(Red_alert_num_slots_used, fp);
811
812         if ( Red_alert_num_slots_used <= 0 ) {
813                 return;
814         }
815
816         Assert(strlen(Red_alert_precursor_mission) > 0 );
817         cfwrite_string(Red_alert_precursor_mission, fp);
818
819         for ( i = 0; i < Red_alert_num_slots_used; i++ ) {
820                 ras = &Red_alert_wingman_status[i];
821                 cfwrite_string(ras->name, fp);
822                 cfwrite_float(ras->hull, fp);
823                 cfwrite_int(ras->ship_class, fp);
824
825                 for ( j = 0; j < MAX_RED_ALERT_SUBSYSTEMS; j++ ) {
826                         cfwrite_float(ras->subsys_current_hits[j], fp);
827                 }
828
829                 for ( j = 0; j < SUBSYSTEM_MAX; j++ ) {
830                         cfwrite_float(ras->subsys_aggregate_current_hits[j], fp);
831                 }
832
833                 for ( j = 0; j < MAX_WL_WEAPONS; j++ ) {
834                         cfwrite_int( ras->wep[j], fp ) ;
835                         cfwrite_int( ras->wep_count[j], fp );
836                 }
837         }
838 }
839
840 // red wingman status out of the specified file
841 void red_alert_read_wingman_status(CFILE *fp, int version)
842 {
843         int                             i, j;
844         red_alert_ship_status *ras;
845
846         Red_alert_num_slots_used = cfread_int(fp);
847
848         if ( Red_alert_num_slots_used <= 0 ) {
849                 return;
850         }
851
852         cfread_string(Red_alert_precursor_mission, MAX_FILENAME_LEN, fp);
853
854         for ( i = 0; i < Red_alert_num_slots_used; i++ ) {
855                 ras = &Red_alert_wingman_status[i];
856                 cfread_string(ras->name, NAME_LENGTH, fp);
857                 ras->hull = cfread_float(fp);
858                 ras->ship_class = cfread_int(fp);
859
860                 for ( j = 0; j < MAX_RED_ALERT_SUBSYSTEMS; j++ ) {
861                         ras->subsys_current_hits[j] = cfread_float(fp);
862                 }
863
864                 for ( j = 0; j < SUBSYSTEM_MAX; j++ ) {
865                         ras->subsys_aggregate_current_hits[j] = cfread_float(fp);
866                 }
867
868                 for ( j = 0; j < MAX_WL_WEAPONS; j++ ) {
869                         ras->wep[j] = cfread_int(fp) ;
870                         ras->wep_count[j] = cfread_int(fp);
871                 }
872         }
873 }
874
875 // return !0 if this is a red alert mission, otherwise return 0
876 int red_alert_mission()
877 {
878         if ( Red_alert_status == RED_ALERT_MISSION ) {
879                 return 1;
880         }
881
882         return 0;
883 }
884
885 // called from sexpression code to start a red alert mission
886 void red_alert_start_mission()
887 {
888         // if we are not in campaign mode, go to debriefing
889 //      if ( !(Game_mode & GM_CAMPAIGN_MODE) ) {
890 //              gameseq_post_event( GS_EVENT_DEBRIEF ); // proceed to debriefing
891 //              return;
892 //      }
893
894         // check player health here.  
895         // if we're dead (or about to die), don't start red alert mission.
896         if (Player_obj->type == OBJ_SHIP) {
897                 if (Player_obj->hull_strength > 0) {
898                         // make sure we don't die
899                         Player_obj->flags |= OF_GUARDIAN;
900
901                         // do normal red alert stuff
902                         Red_alert_new_mission_timestamp = timestamp(RED_ALERT_WARN_TIME);
903
904                         // throw down a sound here to make the warning seem ultra-important
905                         // gamesnd_play_iface(SND_USER_SELECT);
906                         snd_play(&(Snds[SND_DIRECTIVE_COMPLETE]));
907                 }
908         }
909 }
910
911 // called from main game loop to check to see if we should move to a red alert mission
912 int red_alert_check_status()
913 {
914         // if the timestamp is invalid, do nothing.
915         if ( !timestamp_valid(Red_alert_new_mission_timestamp) )
916                 return 0;
917
918         // return if the timestamp hasn't elapsed yet
919         if ( timestamp_elapsed(Red_alert_new_mission_timestamp) ) {
920
921                 // basic premise here is to stop the current mission, and then set the next mission in the campaign
922                 // which better be a red alert mission
923                 if ( Game_mode & GM_CAMPAIGN_MODE ) {
924                         red_alert_store_wingman_status();
925                         mission_goal_fail_incomplete();
926                         mission_campaign_store_goals_and_events();
927                         scoring_level_close();
928                         mission_campaign_eval_next_mission();
929                         mission_campaign_mission_over();
930
931                         // CD CHECK
932                         gameseq_post_event(GS_EVENT_START_GAME);
933                 } else {
934                         gameseq_post_event(GS_EVENT_END_GAME);
935                 }
936         }
937
938         return 1;
939 }
940