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