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