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