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