2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
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
10 * $Logfile: /Freespace2/code/MissionUI/RedAlert.cpp $
15 * Module for Red Alert mission interface and code
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
25 * Revision 1.7 2004/09/20 01:31:44 theoddone33
28 * Revision 1.6 2003/08/03 16:10:29 taylor
29 * cleanup; compile warning fixes
31 * Revision 1.5 2003/05/25 02:30:43 taylor
34 * Revision 1.4 2002/06/09 04:41:23 relnev
35 * added copyright header
37 * Revision 1.3 2002/05/26 22:06:17 relnev
38 * makefile: disable stand_gui for now.
40 * rest: staticize some globals
42 * Revision 1.2 2002/05/03 13:34:33 theoddone33
45 * Revision 1.1.1.1 2002/05/03 03:28:10 root
49 * 16 9/11/99 12:31a Mikek
50 * Bumped up max red alert status ships and status subsystems.
52 * 15 9/06/99 6:38p Dave
53 * Improved CD detection code.
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.
59 * 13 8/11/99 2:17p Jefff
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
66 * 11 7/19/99 2:13p Dave
67 * Added some new strings for Heiko.
69 * 10 7/16/99 12:22p Jefff
70 * Added sound FX to red alert popup
72 * 9 7/09/99 10:32p Dave
73 * Command brief and red alert screens.
75 * 8 5/07/99 10:34a Andsager
76 * Make red alert work in FS2
78 * 7 2/16/99 5:07p Neilk
81 * 6 1/30/99 5:08p Dave
82 * More new hi-res stuff.Support for nice D3D textures.
84 * 5 1/27/99 9:56a Dave
85 * Temporary checkin of beam weapons for Dan to make cool sounds.
87 * 4 11/18/98 11:15a Andsager
88 * Removed old version or red_alert_read_wingman_status
90 * 3 10/13/98 9:29a Dave
91 * Started neatening up freespace.h. Many variables renamed and
92 * reorganized. Added AlphaColors.[h,cpp]
94 * 2 10/07/98 10:53a Dave
97 * 1 10/07/98 10:50a Dave
99 * 21 5/14/98 11:26a Lawrance
100 * ESC will return to the main hall
102 * 20 5/13/98 5:14p Allender
103 * red alert support to go back to previous mission
105 * 19 5/05/98 6:19p Lawrance
106 * Fix problems with "retry mission" for red alerts
108 * 18 5/05/98 3:14p Comet
109 * AL: Fix bug with restoring secondary weapons in red alert mode
111 * 17 5/04/98 10:49p Lawrance
112 * Make sure old pilot files don't cause problems with the new RedAlert
115 * 16 5/04/98 6:06p Lawrance
116 * Make red alert mode work!
118 * 15 5/02/98 4:10p Lawrance
119 * Only use first stage of briefing for red alerts
121 * 14 5/01/98 9:18p Ed
122 * mark all goals and events as failed when red alert moves ahead to next
125 * 13 4/22/98 10:13a John
126 * added assert to trap a bug
128 * 12 4/21/98 12:08a Allender
129 * only make red alert move to next mission in campaign mode
131 * 11 4/03/98 10:31a John
132 * Made briefing and debriefing arrays be malloc'd
134 * 10 3/28/98 2:53p Allender
135 * added hud gauge when entering a red alert mission
137 * 9 3/12/98 10:28p Lawrance
138 * Deal with situation where wingman status may not have been properly
141 * 8 3/12/98 9:44p Allender
142 * go to debrief in red alert when not in campaign mode
144 * 6 3/09/98 9:55p Lawrance
145 * improve some comments
147 * 5 3/09/98 4:41p Lawrance
148 * Fix up some merge problems.
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
154 * 3 3/09/98 4:23p Lawrance
155 * Replay mission, full save/restore of wingman status
157 * 2 3/09/98 12:13a Lawrance
158 * Add support for Red Alert missions
160 * 1 3/08/98 4:54p Lawrance
167 #include "gamesequence.h"
168 #include "missionscreencommon.h"
172 #include "redalert.h"
174 #include "missionbriefcommon.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"
184 /////////////////////////////////////////////////////////////////////////////
185 // Red Alert Mission-Level
186 /////////////////////////////////////////////////////////////////////////////
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;
193 #define RED_ALERT_WARN_TIME 4000 // time to warn user that new orders are coming
195 #define RED_ALERT_NONE 0
196 #define RED_ALERT_MISSION 1
198 #define MAX_RED_ALERT_SLOTS 32
199 #define MAX_RED_ALERT_SUBSYSTEMS 64
200 #define RED_ALERT_EXITED_SHIP_CLASS -1
202 typedef struct red_alert_ship_status
204 char name[NAME_LENGTH];
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;
213 static red_alert_ship_status Red_alert_wingman_status[MAX_RED_ALERT_SLOTS];
214 static char Red_alert_precursor_mission[MAX_FILENAME_LEN];
216 /////////////////////////////////////////////////////////////////////////////
217 // Red Alert Interface
218 /////////////////////////////////////////////////////////////////////////////
220 const char *Red_alert_fname[GR_NUM_RESOLUTIONS] = {
225 const char *Red_alert_mask[GR_NUM_RESOLUTIONS] = {
230 // font to use for "incoming transmission"
231 int Ra_flash_font[GR_NUM_RESOLUTIONS] = {
236 int Ra_flash_y[GR_NUM_RESOLUTIONS] = {
242 static int Ra_flash_coords[GR_NUM_RESOLUTIONS][2] = {
252 #define NUM_BUTTONS 2
254 #define RA_REPLAY_MISSION 0
255 #define RA_CONTINUE 1
257 static ui_button_info Buttons[GR_NUM_RESOLUTIONS][NUM_BUTTONS] = {
260 ui_button_info("RAB_05", 0, 435, -1, -1, 5), // Replay
261 ui_button_info("RAB_04", 553, 411, -1, -1, 4), // Exit
263 ui_button_info("RAB_00", 2, 445, -1, -1, 0),
264 ui_button_info("RAB_01", 575, 432, -1, -1, 1),
268 ui_button_info("2_RAB_00", 4, 712, -1, -1, 0),
269 ui_button_info("2_RAB_01", 920, 691, -1, -1, 1),
274 #define RED_ALERT_NUM_TEXT 3
276 UI_XSTR Red_alert_text[GR_NUM_RESOLUTIONS][RED_ALERT_NUM_TEXT] = {
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 },
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 },
290 // indicies for coordinates
297 static int Text_delay;
299 int Ra_brief_text_wnd_coords[GR_NUM_RESOLUTIONS][4] = {
312 static UI_WINDOW Ui_window;
314 static hud_anim Flash_anim;
316 static int Background_bitmap;
317 static int Red_alert_inited = 0;
319 static int Red_alert_voice;
321 // open and pre-load the stream buffers for the different voice streams
322 void red_alert_voice_load()
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 );
330 // close all the briefing voice streams
331 void red_alert_voice_unload()
333 if ( Red_alert_voice != -1 ) {
334 audiostream_close_file(Red_alert_voice, 0);
335 Red_alert_voice = -1;
339 // start playback of the red alert voice
340 void red_alert_voice_play()
342 if ( Red_alert_voice == -1 ){
343 return; // voice file doesn't exist
346 if ( !Briefing_voice_enabled ) {
350 if ( audiostream_is_playing(Red_alert_voice) ){
354 audiostream_play(Red_alert_voice, Master_voice_volume, 0);
355 Red_alert_voice_started = 1;
358 // stop playback of the red alert voice
359 void red_alert_voice_stop()
361 if ( Red_alert_voice == -1 )
364 audiostream_stop(Red_alert_voice); // stream is automatically rewound
367 // a button was pressed, deal with it
368 void red_alert_button_pressed(int n)
372 if(game_do_cd_mission_check(Game_current_mission_filename)){
373 gameseq_post_event(GS_EVENT_ENTER_GAME);
375 gameseq_post_event(GS_EVENT_MAIN_MENU);
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);
389 if(game_do_cd_mission_check(Game_current_mission_filename)){
390 gameseq_post_event(GS_EVENT_START_GAME);
392 gameseq_post_event(GS_EVENT_MAIN_MENU);
395 gamesnd_play_iface(SND_GENERAL_FAIL);
401 // blit "incoming transmission"
402 #define RA_FLASH_CYCLE 0.25f
403 float Ra_flash_time = 0.0f;
405 void red_alert_blit_title()
407 const char *str = XSTR("Incoming Transmission", 1406);
410 // get the string size
411 gr_set_font(Ra_flash_font[gr_screen.res]);
412 gr_get_string_size(&w, &h, str);
417 gr_init_alphacolor(&flash_color, (int)(255.0f * (Ra_flash_time / RA_FLASH_CYCLE)), 0, 0, 255, AC_TYPE_HUD);
419 gr_init_alphacolor(&flash_color, (int)(255.0f * (1.0f - (Ra_flash_time / RA_FLASH_CYCLE))), 0, 0, 255, AC_TYPE_HUD);
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);
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;
436 // back to the original font
440 // Called once when red alert interface is started
441 void red_alert_init()
446 if ( Red_alert_inited ) {
451 common_set_interface_palette("ControlConfigPalette"); // set the interface palette
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]);
456 for (i=0; i<NUM_BUTTONS; i++) {
457 b = &Buttons[gr_screen.res][i];
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);
468 for(i=0; i<RED_ALERT_NUM_TEXT; i++){
469 Ui_window.add_XSTR(&Red_alert_text[gr_screen.res][i]);
473 // set up red alert hotkeys
474 Buttons[gr_screen.res][RA_CONTINUE].button.set_hotkey(KEY_CTRLED | SDLK_RETURN);
476 // load in background image and flashing red alert animation
477 Background_bitmap = bm_load(Red_alert_fname[gr_screen.res]);
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);
484 Red_alert_voice = -1;
487 Briefing = &Briefings[0];
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);
495 red_alert_voice_load();
497 Text_delay = timestamp(200);
499 Red_alert_voice_started = 0;
500 Red_alert_inited = 1;
503 // Called once when the red alert interface is exited
504 void red_alert_close()
506 if (Red_alert_inited) {
508 red_alert_voice_stop();
509 red_alert_voice_unload();
511 if (Background_bitmap >= 0) {
512 bm_unload(Background_bitmap);
517 hud_anim_release(&Flash_anim);
519 common_free_interface_palette(); // restore game palette
523 Red_alert_inited = 0;
526 // called once per frame when game state is GS_STATE_RED_ALERT
527 void red_alert_do_frame(float frametime)
531 // ensure that the red alert interface has been initialized
532 if (!Red_alert_inited) {
537 k = Ui_window.process() & ~KEY_DEBUGGED;
540 // gameseq_post_event(GS_EVENT_ENTER_GAME);
541 gameseq_post_event(GS_EVENT_MAIN_MENU);
545 for (i=0; i<NUM_BUTTONS; i++){
546 if (Buttons[gr_screen.res][i].button.pressed()){
547 red_alert_button_pressed(i);
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);
559 hud_anim_render(&Flash_anim, frametime);
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);
571 red_alert_voice_play();
575 // blit incoming transmission
576 red_alert_blit_title();
581 // set the red alert status for the current mission
582 void red_alert_set_status(int status)
584 Red_alert_status = status;
585 Red_alert_new_mission_timestamp = timestamp(-1); // make invalid
588 // Store a ships weapons into a wingman status structure
589 void red_alert_store_weapons(red_alert_ship_status *ras, ship_weapon *swp)
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;
602 ras->wep_count[i] = -1;
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];
612 ras->wep_count[sidx] = -1;
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)
622 // restore from ship_exited
623 if (ras->ship_class == RED_ALERT_EXITED_SHIP_CLASS) {
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];
634 swp->num_primary_banks = j;
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];
645 swp->num_secondary_banks = j;
648 void red_alert_bash_subsys_status(red_alert_ship_status *ras, ship *shipp)
653 // restore from ship_exited
654 if (ras->ship_class == RED_ALERT_EXITED_SHIP_CLASS) {
658 ss = GET_FIRST(&shipp->subsys_list);
659 while ( ss != END_OF_LIST( &shipp->subsys_list ) ) {
661 if ( count >= MAX_RED_ALERT_SUBSYSTEMS ) {
662 Int3(); // ran out of subsystems
666 ss->current_hits = ras->subsys_current_hits[count];
667 if (ss->current_hits <= 0) {
668 ss->submodel_info_1.blown_off = 1;
677 for ( i = 0; i < SUBSYSTEM_MAX; i++ ) {
678 shipp->subsys_info[i].current_hits = ras->subsys_aggregate_current_hits[i];
683 void red_alert_store_subsys_status(red_alert_ship_status *ras, ship *shipp)
692 ss = GET_FIRST(&shipp->subsys_list);
693 while ( ss != END_OF_LIST( &shipp->subsys_list ) ) {
695 if ( count >= MAX_RED_ALERT_SUBSYSTEMS ) {
696 Int3(); // ran out of subsystems
700 ras->subsys_current_hits[count] = ss->current_hits;
708 for ( i = 0; i < SUBSYSTEM_MAX; i++ ) {
709 ras->subsys_aggregate_current_hits[i] = shipp->subsys_info[i].current_hits;
714 // Record the current state of the players wingman
715 void red_alert_store_wingman_status()
718 red_alert_ship_status *ras;
722 Red_alert_num_slots_used = 0;
724 // store the mission filename for the red alert precursor mission
725 strcpy(Red_alert_precursor_mission, Game_current_mission_filename);;
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];
733 if ( shipp->flags & SF_DYING ) {
737 if ( Red_alert_num_slots_used >= MAX_RED_ALERT_SLOTS ) {
738 Int3(); // ran out of red alert slots
742 if ( !(shipp->flags & SF_FROM_PLAYER_WING) && !(shipp->flags & SF_RED_ALERT_STORE_STATUS) ) {
746 ras = &Red_alert_wingman_status[Red_alert_num_slots_used];
747 Red_alert_num_slots_used++;
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);
756 // store exited ships that did not die
757 for (int idx=0; idx<MAX_EXITED_SHIPS; idx++) {
759 if ( Red_alert_num_slots_used >= MAX_RED_ALERT_SLOTS ) {
760 Int3(); // ran out of red alert slots
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++;
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);
776 Assert(Red_alert_num_slots_used > 0);
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)
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);
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] );
793 // Take the stored wingman status information, and adjust the player wing ships accordingly
794 void red_alert_bash_wingman_status()
798 red_alert_ship_status *ras;
802 if ( !(Game_mode & GM_CAMPAIGN_MODE) ) {
806 if ( Red_alert_num_slots_used <= 0 ) {
810 // go through all ships in the game, and see if there is red alert status data for any
812 int remove_list[MAX_RED_ALERT_SLOTS];
813 int remove_count = 0;
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];
820 if ( !(shipp->flags & SF_FROM_PLAYER_WING) && !(shipp->flags & SF_RED_ALERT_STORE_STATUS) ) {
826 for ( i = 0; i < Red_alert_num_slots_used; i++ ) {
827 ras = &Red_alert_wingman_status[i];
829 if ( !stricmp(ras->name, shipp->ship_name) ) {
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;
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);
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);
847 if ( !found_match ) {
848 remove_list[remove_count++] = SHIP_INDEX(shipp);
853 for ( i = 0; i < remove_count; i++ ) {
855 red_alert_delete_ship(&Ships[remove_list[i]]);
859 // write wingman status out to the specified file
860 void red_alert_write_wingman_status(CFILE *fp)
863 red_alert_ship_status *ras;
865 cfwrite_int(Red_alert_num_slots_used, fp);
867 if ( Red_alert_num_slots_used <= 0 ) {
871 Assert(strlen(Red_alert_precursor_mission) > 0 );
872 cfwrite_string(Red_alert_precursor_mission, fp);
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);
880 for ( j = 0; j < MAX_RED_ALERT_SUBSYSTEMS; j++ ) {
881 cfwrite_float(ras->subsys_current_hits[j], fp);
884 for ( j = 0; j < SUBSYSTEM_MAX; j++ ) {
885 cfwrite_float(ras->subsys_aggregate_current_hits[j], fp);
888 for ( j = 0; j < MAX_WL_WEAPONS; j++ ) {
889 cfwrite_int( ras->wep[j], fp ) ;
890 cfwrite_int( ras->wep_count[j], fp );
895 // red wingman status out of the specified file
896 void red_alert_read_wingman_status(CFILE *fp, int version)
899 red_alert_ship_status *ras;
901 Red_alert_num_slots_used = cfread_int(fp);
903 if ( Red_alert_num_slots_used <= 0 ) {
907 cfread_string(Red_alert_precursor_mission, MAX_FILENAME_LEN, fp);
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);
915 for ( j = 0; j < MAX_RED_ALERT_SUBSYSTEMS; j++ ) {
916 ras->subsys_current_hits[j] = cfread_float(fp);
919 for ( j = 0; j < SUBSYSTEM_MAX; j++ ) {
920 ras->subsys_aggregate_current_hits[j] = cfread_float(fp);
923 for ( j = 0; j < MAX_WL_WEAPONS; j++ ) {
924 ras->wep[j] = cfread_int(fp) ;
925 ras->wep_count[j] = cfread_int(fp);
930 // return !0 if this is a red alert mission, otherwise return 0
931 int red_alert_mission()
933 if ( Red_alert_status == RED_ALERT_MISSION ) {
940 // called from sexpression code to start a red alert mission
941 void red_alert_start_mission()
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
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;
956 // do normal red alert stuff
957 Red_alert_new_mission_timestamp = timestamp(RED_ALERT_WARN_TIME);
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]));
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()
969 // if the timestamp is invalid, do nothing.
970 if ( !timestamp_valid(Red_alert_new_mission_timestamp) )
973 // return if the timestamp hasn't elapsed yet
974 if ( timestamp_elapsed(Red_alert_new_mission_timestamp) ) {
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();
987 gameseq_post_event(GS_EVENT_START_GAME);
989 gameseq_post_event(GS_EVENT_END_GAME);