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