/* * Copyright (C) Volition, Inc. 1999. All rights reserved. * * All source code herein is the property of Volition, Inc. You may not sell * or otherwise commercially exploit the source or things you created based on the * source. * */ /* * $Logfile: /Freespace2/code/MissionUI/RedAlert.cpp $ * $Revision: 2.25 $ * $Date: 2007-10-28 15:38:16 $ * $Author: karajorma $ * * Module for Red Alert mission interface and code * * $Log: not supported by cvs2svn $ * Revision 2.24 2007/03/22 20:47:33 taylor * make sure that we set/reset the red alert data properly (Mantis #1255) * * Revision 2.23 2006/10/06 09:55:36 taylor * For redalert stored data be sure that dead ships don't come back, and departed ships come back just as they left (Mantis bug #810) * * Revision 2.22 2006/09/13 03:17:59 taylor * be sure to setup/reset ship team data too when moving to a red-alert mission (Mantis bug 1042) * (this will hopefully fix Mantis bug 860 as well, which is a separate but related issue) * * Revision 2.21 2005/11/16 05:46:27 taylor * bunch of error checking and code cleanup for the team stuff in ship/weapon select * * Revision 2.20 2005/10/10 17:21:06 taylor * remove NO_NETWORK * * Revision 2.19 2005/09/26 04:08:54 Goober5000 * some more cleanup * --Goober5000 * * Revision 2.18 2005/09/09 05:12:44 Goober5000 * "no briefing" flag now works for red-alert missions too * --Goober5000 * * Revision 2.17 2005/07/25 05:24:16 Goober5000 * cleaned up some command line and mission flag stuff * --Goober5000 * * Revision 2.16 2005/07/02 19:43:55 taylor * ton of non-standard resolution fixes * * Revision 2.15 2005/06/03 06:39:26 taylor * better audio pause/unpause support when game window loses focus or is minimized * * Revision 2.14 2005/04/03 08:48:30 Goober5000 * brought weapon loadout banks into agreement with ship info banks * improved error reporting on apply-to-all * --Goober5000 * * Revision 2.13 2005/03/27 12:28:34 Goober5000 * clarified max hull/shield strength names and added ship guardian thresholds * --Goober5000 * * Revision 2.12 2005/03/24 23:29:33 taylor * (re)move some uneeded variables to fix compiler warnings * * Revision 2.11 2005/02/23 04:55:08 taylor * more bm_unload() -> bm_release() changes * * Revision 2.10 2004/10/31 21:53:24 taylor * new pilot code support, no-multiplayer and compiler warning fixes, center mouse cursor for redalert missions * * Revision 2.9 2004/07/26 20:47:40 Kazan * remove MCD complete * * Revision 2.8 2004/07/12 16:32:56 Kazan * MCD - define _MCD_CHECK to use memory tracking * * Revision 2.7 2004/03/05 09:01:55 Goober5000 * Uber pass at reducing #includes * --Goober5000 * * Revision 2.6 2004/02/04 09:02:43 Goober5000 * got rid of unnecessary double semicolons * --Goober5000 * * Revision 2.5 2003/09/07 18:14:54 randomtiger * Checked in new speech code and calls from relevent modules to make it play. * Should all work now if setup properly with version 2.4 of the launcher. * FS2_SPEECH can be used to make the speech code compile if you have SAPI 5.1 SDK installed. * Otherwise the compile flag should not be set and it should all compile OK. * * - RT * * Revision 2.4 2003/09/05 04:25:28 Goober5000 * well, let's see here... * * * persistent variables * * rotating gun barrels * * positive/negative numbers fixed * * sexps to trigger whether the player is controlled by AI * * sexp for force a subspace jump * * I think that's it :) * --Goober5000 * * Revision 2.3 2003/03/18 10:07:04 unknownplayer * The big DX/main line merge. This has been uploaded to the main CVS since I can't manage to get it to upload to the DX branch. Apologies to all who may be affected adversely, but I'll work to debug it as fast as I can. * * Revision 2.2 2002/12/10 05:43:33 Goober5000 * Full-fledged ballistic primary support added! Try it and see! :) * * Revision 2.1.2.1 2002/09/24 18:56:44 randomtiger * DX8 branch commit * * This is the scub of UP's previous code with the more up to date RT code. * For full details check previous dev e-mails * * Revision 2.1 2002/08/01 01:41:07 penguin * The big include file move * * Revision 2.0 2002/06/03 04:02:25 penguin * Warpcore CVS sync * * Revision 1.3 2002/05/09 23:02:59 mharris * Not using default values for audiostream functions, since they may * be macros (if NO_SOUND is defined) * * Revision 1.2 2002/05/07 02:59:29 mharris * make Buttons[] static * * Revision 1.1 2002/05/02 18:03:10 mharris * Initial checkin - converted filenames and includes to lower case * * * 16 9/11/99 12:31a Mikek * Bumped up max red alert status ships and status subsystems. * * 15 9/06/99 6:38p Dave * Improved CD detection code. * * 14 8/24/99 5:27p Andsager * Make subsystems with zero strength before mission blown off. Protect * red alert pilot against dying between orders and jump. * * 13 8/11/99 2:17p Jefff * changed a string * * 12 7/19/99 8:56p Andsager * Added Ship_exited red_alert_carry flag and hull strength for RED ALERT * carry over of Exited_ships * * 11 7/19/99 2:13p Dave * Added some new strings for Heiko. * * 10 7/16/99 12:22p Jefff * Added sound FX to red alert popup * * 9 7/09/99 10:32p Dave * Command brief and red alert screens. * * 8 5/07/99 10:34a Andsager * Make red alert work in FS2 * * 7 2/16/99 5:07p Neilk * Added hires support * * 6 1/30/99 5:08p Dave * More new hi-res stuff.Support for nice D3D textures. * * 5 1/27/99 9:56a Dave * Temporary checkin of beam weapons for Dan to make cool sounds. * * 4 11/18/98 11:15a Andsager * Removed old version or red_alert_read_wingman_status * * 3 10/13/98 9:29a Dave * Started neatening up freespace.h. Many variables renamed and * reorganized. Added AlphaColors.[h,cpp] * * 2 10/07/98 10:53a Dave * Initial checkin. * * 1 10/07/98 10:50a Dave * * 21 5/14/98 11:26a Lawrance * ESC will return to the main hall * * 20 5/13/98 5:14p Allender * red alert support to go back to previous mission * * 19 5/05/98 6:19p Lawrance * Fix problems with "retry mission" for red alerts * * 18 5/05/98 3:14p Comet * AL: Fix bug with restoring secondary weapons in red alert mode * * 17 5/04/98 10:49p Lawrance * Make sure old pilot files don't cause problems with the new RedAlert * data format * * 16 5/04/98 6:06p Lawrance * Make red alert mode work! * * 15 5/02/98 4:10p Lawrance * Only use first stage of briefing for red alerts * * 14 5/01/98 9:18p Ed * mark all goals and events as failed when red alert moves ahead to next * mission * * 13 4/22/98 10:13a John * added assert to trap a bug * * 12 4/21/98 12:08a Allender * only make red alert move to next mission in campaign mode * * 11 4/03/98 10:31a John * Made briefing and debriefing arrays be malloc'd * * 10 3/28/98 2:53p Allender * added hud gauge when entering a red alert mission * * 9 3/12/98 10:28p Lawrance * Deal with situation where wingman status may not have been properly * saved * * 8 3/12/98 9:44p Allender * go to debrief in red alert when not in campaign mode * * 6 3/09/98 9:55p Lawrance * improve some comments * * 5 3/09/98 4:41p Lawrance * Fix up some merge problems. * * 4 3/09/98 4:30p Allender * multiplayer secondary weapon changes. red-alert and cargo-known-delay * sexpressions. Add time cargo revealed to ship structure * * 3 3/09/98 4:23p Lawrance * Replay mission, full save/restore of wingman status * * 2 3/09/98 12:13a Lawrance * Add support for Red Alert missions * * 1 3/08/98 4:54p Lawrance * * $NoKeywords: $ */ #include "missionui/redalert.h" #include "model/model.h" #include "gamesnd/gamesnd.h" #include "gamesequence/gamesequence.h" #include "missionui/missionscreencommon.h" #include "missionui/missionweaponchoice.h" #include "io/key.h" #include "graphics/font.h" #include "mission/missionbriefcommon.h" #include "io/timer.h" #include "mission/missioncampaign.h" #include "mission/missiongoals.h" #include "globalincs/linklist.h" #include "hud/hudwingmanstatus.h" #include "sound/audiostr.h" #include "freespace2/freespace.h" #include "globalincs/alphacolors.h" #include "sound/fsspeech.h" #include "ship/ship.h" #include "weapon/weapon.h" #include "cfile/cfile.h" #include "io/mouse.h" ///////////////////////////////////////////////////////////////////////////// // Red Alert Mission-Level ///////////////////////////////////////////////////////////////////////////// static int Red_alert_new_mission_timestamp; // timestamp used to give user a little warning for red alerts static int Red_alert_num_slots_used = 0; static int Red_alert_voice_started; #define RED_ALERT_WARN_TIME 4000 // time to warn user that new orders are coming #define MAX_RED_ALERT_SLOTS 32 #define MAX_RED_ALERT_SUBSYSTEMS 64 #define RED_ALERT_EXITED_SHIP_CLASS -1 typedef struct red_alert_ship_status { char name[NAME_LENGTH]; float hull; int ship_class; float subsys_current_hits[MAX_RED_ALERT_SUBSYSTEMS]; float subsys_aggregate_current_hits[SUBSYSTEM_MAX]; int wep[MAX_SHIP_WEAPONS]; int wep_count[MAX_SHIP_WEAPONS]; } red_alert_ship_status; static red_alert_ship_status Red_alert_wingman_status[MAX_RED_ALERT_SLOTS]; static char Red_alert_precursor_mission[MAX_FILENAME_LEN]; ///////////////////////////////////////////////////////////////////////////// // Red Alert Interface ///////////////////////////////////////////////////////////////////////////// char *Red_alert_fname[GR_NUM_RESOLUTIONS] = { "RedAlert", "2_RedAlert" }; char *Red_alert_mask[GR_NUM_RESOLUTIONS] = { "RedAlert-m", "2_RedAlert-m" }; // font to use for "incoming transmission" int Ra_flash_font[GR_NUM_RESOLUTIONS] = { FONT2, FONT2 }; int Ra_flash_y[GR_NUM_RESOLUTIONS] = { 140, 200 }; /* static int Ra_flash_coords[GR_NUM_RESOLUTIONS][2] = { { 61, 108 // GR_640 }, { 61, 108 // GR_1024 } }; */ #define NUM_BUTTONS 2 #define RA_REPLAY_MISSION 0 #define RA_CONTINUE 1 static ui_button_info Buttons[GR_NUM_RESOLUTIONS][NUM_BUTTONS] = { { // GR_640 ui_button_info("RAB_00", 2, 445, -1, -1, 0), ui_button_info("RAB_01", 575, 432, -1, -1, 1), }, { // GR_1024 ui_button_info("2_RAB_00", 4, 712, -1, -1, 0), ui_button_info("2_RAB_01", 920, 691, -1, -1, 1), } }; #define RED_ALERT_NUM_TEXT 3 UI_XSTR Red_alert_text[GR_NUM_RESOLUTIONS][RED_ALERT_NUM_TEXT] = { { // GR_640 { "Replay", 1405, 46, 451, UI_XSTR_COLOR_PINK, -1, &Buttons[0][RA_REPLAY_MISSION].button }, { "Previous Mission", 1452, 46, 462, UI_XSTR_COLOR_PINK, -1, &Buttons[0][RA_REPLAY_MISSION].button }, { "Continue", 1069, 564, 413, UI_XSTR_COLOR_PINK, -1, &Buttons[0][RA_CONTINUE].button }, }, { // GR_1024 { "Replay", 1405, 75, 722, UI_XSTR_COLOR_PINK, -1, &Buttons[1][RA_REPLAY_MISSION].button }, { "Previous Mission", 1452, 75, 733, UI_XSTR_COLOR_PINK, -1, &Buttons[1][RA_REPLAY_MISSION].button }, { "Continue", 1069, 902, 661, UI_XSTR_COLOR_PINK, -1, &Buttons[1][RA_CONTINUE].button }, } }; // indicies for coordinates #define RA_X_COORD 0 #define RA_Y_COORD 1 #define RA_W_COORD 2 #define RA_H_COORD 3 static int Text_delay; int Ra_brief_text_wnd_coords[GR_NUM_RESOLUTIONS][4] = { { 14, 151, 522, 289 }, { 52, 241, 785, 463 } }; static UI_WINDOW Ui_window; // static hud_anim Flash_anim; static int Background_bitmap; static int Red_alert_inited = 0; static int Red_alert_voice; // open and pre-load the stream buffers for the different voice streams void red_alert_voice_load() { Assert( Briefing != NULL ); if ( strnicmp(Briefing->stages[0].voice, NOX("none"), 4) && (strlen(Briefing->stages[0].voice) > 0) ) { Red_alert_voice = audiostream_open( Briefing->stages[0].voice, ASF_VOICE ); } } // close all the briefing voice streams void red_alert_voice_unload() { if ( Red_alert_voice != -1 ) { audiostream_close_file(Red_alert_voice, 0); Red_alert_voice = -1; } } // start playback of the red alert voice void red_alert_voice_play() { if ( Red_alert_voice == -1 ){ fsspeech_play(FSSPEECH_FROM_BRIEFING, Briefing->stages[0].new_text); return; // voice file doesn't exist } if ( !Briefing_voice_enabled ) { return; } if ( audiostream_is_playing(Red_alert_voice) ){ return; } audiostream_play(Red_alert_voice, Master_voice_volume, 0); Red_alert_voice_started = 1; } // stop playback of the red alert voice void red_alert_voice_stop() { if ( Red_alert_voice == -1 ) return; audiostream_stop(Red_alert_voice, 1, 0); // stream is automatically rewound } // pausing and unpausing of red alert voice void red_alert_voice_pause() { if ( Red_alert_voice == -1 ) return; if ( !Red_alert_voice_started ) return; audiostream_pause(Red_alert_voice); } void red_alert_voice_unpause() { if ( Red_alert_voice == -1 ) return; if ( !Red_alert_voice_started ) return; audiostream_unpause(Red_alert_voice); } // a button was pressed, deal with it void red_alert_button_pressed(int n) { switch (n) { case RA_CONTINUE: // warp the mouse cursor the the middle of the screen for those who control with a mouse mouse_set_pos( gr_screen.max_w/2, gr_screen.max_h/2 ); if(game_do_cd_mission_check(Game_current_mission_filename)){ gameseq_post_event(GS_EVENT_ENTER_GAME); } else { gameseq_post_event(GS_EVENT_MAIN_MENU); } break; case RA_REPLAY_MISSION: if ( Game_mode & GM_CAMPAIGN_MODE ) { // TODO: make call to campaign code to set correct mission for loading // mission_campaign_play_previous_mission(Red_alert_precursor_mission); if ( !mission_campaign_previous_mission() ) { gamesnd_play_iface(SND_GENERAL_FAIL); break; } // CD CHECK if(game_do_cd_mission_check(Game_current_mission_filename)){ gameseq_post_event(GS_EVENT_START_GAME); } else { gameseq_post_event(GS_EVENT_MAIN_MENU); } } else { gamesnd_play_iface(SND_GENERAL_FAIL); } break; } } // blit "incoming transmission" #define RA_FLASH_CYCLE 0.25f float Ra_flash_time = 0.0f; int Ra_flash_up = 0; void red_alert_blit_title() { char *str = XSTR("Incoming Transmission", 1406); int w, h; // get the string size gr_set_font(Ra_flash_font[gr_screen.res]); gr_get_string_size(&w, &h, str); // set alpha color color flash_color; if(Ra_flash_up){ gr_init_alphacolor(&flash_color, (int)(255.0f * (Ra_flash_time / RA_FLASH_CYCLE)), 0, 0, 255); } else { gr_init_alphacolor(&flash_color, (int)(255.0f * (1.0f - (Ra_flash_time / RA_FLASH_CYCLE))), 0, 0, 255); } // draw gr_set_color_fast(&flash_color); 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); gr_set_color_fast(&Color_normal); // increment flash time Ra_flash_time += flFrametime; if(Ra_flash_time >= RA_FLASH_CYCLE){ Ra_flash_time = 0.0f; Ra_flash_up = !Ra_flash_up; } // back to the original font gr_set_font(FONT1); } // Called once when red alert interface is started void red_alert_init() { int i; ui_button_info *b; if ( Red_alert_inited ) { return; } // common_set_interface_palette("ControlConfigPalette"); // set the interface palette Ui_window.create(0, 0, gr_screen.max_w_unscaled, gr_screen.max_h_unscaled, 0); Ui_window.set_mask_bmap(Red_alert_mask[gr_screen.res]); for (i=0; ibutton.create(&Ui_window, "", b->x, b->y, 60, 30, 0, 1); // set up callback for when a mouse first goes over a button b->button.set_highlight_action(common_play_highlight_sound); b->button.set_bmaps(b->filename); b->button.link_hotspot(b->hotspot); } // all text for(i=0; inum_stages > 0 ) { Assert(Briefing->stages[0].new_text); brief_color_text_init(Briefing->stages[0].new_text, Ra_brief_text_wnd_coords[gr_screen.res][RA_W_COORD], 0); } red_alert_voice_load(); // we have to reset/setup the shipselect and weaponselect pointers before moving on ship_select_common_init(); weapon_select_common_init(); Text_delay = timestamp(200); Red_alert_voice_started = 0; Red_alert_inited = 1; } // Called once when the red alert interface is exited void red_alert_close() { if (Red_alert_inited) { red_alert_voice_stop(); red_alert_voice_unload(); weapon_select_close_team(); if (Background_bitmap >= 0) { bm_release(Background_bitmap); } Ui_window.destroy(); // bm_unload(&Flash_anim); common_free_interface_palette(); // restore game palette game_flush(); } Red_alert_inited = 0; fsspeech_stop(); } // called once per frame when game state is GS_STATE_RED_ALERT void red_alert_do_frame(float frametime) { int i, k; // ensure that the red alert interface has been initialized if (!Red_alert_inited) { Int3(); return; } // commit if skipping briefing, but not in multi - Goober5000 if (!(Game_mode & GM_MULTIPLAYER)) { if (The_mission.flags & MISSION_FLAG_NO_BRIEFING) { commit_pressed(); return; } } k = Ui_window.process() & ~KEY_DEBUGGED; switch (k) { case KEY_ESC: // gameseq_post_event(GS_EVENT_ENTER_GAME); gameseq_post_event(GS_EVENT_MAIN_MENU); break; } // end switch for (i=0; i= 0) { gr_set_bitmap(Background_bitmap); gr_bitmap(0, 0); } Ui_window.draw(); // hud_anim_render(&Flash_anim, frametime); gr_set_font(FONT1); if ( timestamp_elapsed(Text_delay) ) { int finished_wipe = 0; if ( Briefing->num_stages > 0 ) { 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); } if (finished_wipe) { red_alert_voice_play(); } } // blit incoming transmission red_alert_blit_title(); gr_flip(); } // set the red alert status for the current mission void red_alert_invalidate_timestamp() { Red_alert_new_mission_timestamp = timestamp(-1); // make invalid } // Store a ships weapons into a wingman status structure void red_alert_store_weapons(red_alert_ship_status *ras, ship_weapon *swp) { int i, sidx; weapon_info *wip; if (swp == NULL) { // just set defaults for (i = 0; i < MAX_SHIP_WEAPONS; i++) { ras->wep[i] = -1; ras->wep_count[i] = 0; } return; } // edited to accommodate ballistics - Goober5000 for (i = 0; i < MAX_SHIP_PRIMARY_BANKS; i++) { wip = &Weapon_info[swp->primary_bank_weapons[i]]; ras->wep[i] = swp->primary_bank_weapons[i]; if (ras->wep[i] >= 0) { if (wip->wi_flags2 & WIF2_BALLISTIC) { // to avoid triggering the below condition: this way, minimum ammo will be 2... // since the red-alert representation of a conventional primary is 0 -> not used, // 1 -> used, I added the representation 2 and above -> ballistic primary ras->wep_count[i] = swp->primary_bank_ammo[i] + 2; } else { ras->wep_count[i] = 1; } } else { ras->wep_count[i] = -1; } } for (i = 0; i < MAX_SHIP_SECONDARY_BANKS; i++) { sidx = i+MAX_SHIP_PRIMARY_BANKS; ras->wep[sidx] = swp->secondary_bank_weapons[i]; if (ras->wep[sidx] >= 0) ras->wep_count[sidx] = swp->secondary_bank_ammo[i]; else ras->wep_count[sidx] = -1; } } // Take the weapons stored in a wingman_status struct, and bash them into the ship weapons struct void red_alert_bash_weapons(red_alert_ship_status *ras, ship_weapon *swp) { int i, j, sidx; // restore from ship_exited if (ras->ship_class == RED_ALERT_EXITED_SHIP_CLASS) return; // modified to accommodate ballistics - Goober5000 j = 0; for (i = 0; i < MAX_SHIP_PRIMARY_BANKS; i++) { if ( (ras->wep_count[i] > 0) && (ras->wep[i] >= 0) ) { swp->primary_bank_weapons[j] = ras->wep[i]; if (ras->wep_count[i] > 1) // this is a ballistic primary (!!!) swp->primary_bank_ammo[j] = ras->wep_count[i] - 2; // to compensate for storage else swp->primary_bank_ammo[i] = 0; j++; } } swp->num_primary_banks = j; j = 0; for (i = 0; i < MAX_SHIP_SECONDARY_BANKS; i++) { sidx = i+MAX_SHIP_PRIMARY_BANKS; if (ras->wep[sidx] >= 0) { swp->secondary_bank_weapons[j] = ras->wep[sidx]; swp->secondary_bank_ammo[j] = ras->wep_count[sidx]; j++; } } swp->num_secondary_banks = j; } void red_alert_bash_subsys_status(red_alert_ship_status *ras, ship *shipp) { ship_subsys *ss; int i, count = 0; // restore from ship_exited if (ras->ship_class == RED_ALERT_EXITED_SHIP_CLASS) return; ss = GET_FIRST(&shipp->subsys_list); while ( ss != END_OF_LIST( &shipp->subsys_list ) ) { if ( count >= MAX_RED_ALERT_SUBSYSTEMS ) { Int3(); // ran out of subsystems break; } // NOTE: we cast to int here in order to get rid of float precision issues with the comparison if ((int)ras->subsys_current_hits[count] != -1) { ss->current_hits = ras->subsys_current_hits[count]; if (ss->current_hits <= 0) ss->submodel_info_1.blown_off = 1; } ss = GET_NEXT( ss ); count++; } for (i = 0; i < SUBSYSTEM_MAX; i++) { // NOTE: we cast to int here in order to get rid of float precision issues with the comparison if ((int)ras->subsys_aggregate_current_hits[i] == -1) continue; shipp->subsys_info[i].current_hits = ras->subsys_aggregate_current_hits[i]; } } void red_alert_store_subsys_status(red_alert_ship_status *ras, ship *shipp) { ship_subsys *ss; int i, count = 0; if (shipp == NULL) { // just set defaults and bail // this assumes that current hits will never equal -1.0f (which it shouldn't) for (i = 0; i < MAX_RED_ALERT_SUBSYSTEMS; i++) ras->subsys_current_hits[i] = -1.0f; for (i = 0; i < SUBSYSTEM_MAX; i++) ras->subsys_aggregate_current_hits[i] = -1.0f; return; } ss = GET_FIRST(&shipp->subsys_list); while ( ss != END_OF_LIST( &shipp->subsys_list ) ) { if ( count >= MAX_RED_ALERT_SUBSYSTEMS ) { Int3(); // ran out of subsystems break; } ras->subsys_current_hits[count] = ss->current_hits; ss = GET_NEXT( ss ); count++; } for (i = 0; i < SUBSYSTEM_MAX; i++) ras->subsys_aggregate_current_hits[i] = shipp->subsys_info[i].current_hits; } // Record the current state of the players wingman void red_alert_store_wingman_status() { ship *shipp; red_alert_ship_status *ras; ship_obj *so; object *ship_objp; Red_alert_num_slots_used = 0; // store the mission filename for the red alert precursor mission strcpy(Red_alert_precursor_mission, Game_current_mission_filename); // store status for all existing ships for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) { ship_objp = &Objects[so->objnum]; Assert(ship_objp->type == OBJ_SHIP); shipp = &Ships[ship_objp->instance]; if ( shipp->flags & SF_DYING ) { continue; } if ( Red_alert_num_slots_used >= MAX_RED_ALERT_SLOTS ) { Int3(); // ran out of red alert slots continue; } if ( !(shipp->flags & SF_FROM_PLAYER_WING) && !(shipp->flags & SF_RED_ALERT_STORE_STATUS) ) { continue; } ras = &Red_alert_wingman_status[Red_alert_num_slots_used]; Red_alert_num_slots_used++; strcpy(ras->name, shipp->ship_name); ras->hull = Objects[shipp->objnum].hull_strength; ras->ship_class = shipp->ship_info_index; red_alert_store_weapons(ras, &shipp->weapons); red_alert_store_subsys_status(ras, shipp); } // store exited ships that did not die for (int idx=0; idx= MAX_RED_ALERT_SLOTS ) { Int3(); // ran out of red alert slots continue; } if (Ships_exited[idx].flags & SEF_RED_ALERT_CARRY) { ras = &Red_alert_wingman_status[Red_alert_num_slots_used]; Red_alert_num_slots_used++; strcpy(ras->name, Ships_exited[idx].ship_name); ras->hull = float(Ships_exited[idx].hull_strength); // if a ship has been destroyed or removed manually by the player, then mark it as such ... if ( (Ships_exited[idx].flags & SEF_DESTROYED) || (Ships_exited[idx].flags & SEF_PLAYER_DELETED) ) { ras->ship_class = RED_ALERT_EXITED_SHIP_CLASS; } // ... otherwise we want to make sure and carry over the ship class else { Assert( Ships_exited[idx].ship_class >= 0 ); ras->ship_class = Ships_exited[idx].ship_class; } red_alert_store_weapons(ras, NULL); red_alert_store_subsys_status(ras, NULL); } } Assert(Red_alert_num_slots_used > 0); } // Delete a ship in a red alert mission (since it must have died/departed in the previous mission) void red_alert_delete_ship(ship *shipp) { if ( (shipp->wing_status_wing_index >= 0) && (shipp->wing_status_wing_pos >= 0) ) { hud_set_wingman_status_dead(shipp->wing_status_wing_index, shipp->wing_status_wing_pos); } ship_add_exited_ship( shipp, SEF_PLAYER_DELETED ); obj_delete(shipp->objnum); if ( shipp->wingnum >= 0 ) { ship_wing_cleanup( shipp-Ships, &Wings[shipp->wingnum] ); } } // Take the stored wingman status information, and adjust the player wing ships accordingly void red_alert_bash_wingman_status() { int i; ship *shipp; red_alert_ship_status *ras; ship_obj *so; object *ship_objp; if ( !(Game_mode & GM_CAMPAIGN_MODE) ) { return; } if ( Red_alert_num_slots_used <= 0 ) { return; } // go through all ships in the game, and see if there is red alert status data for any int remove_list[MAX_RED_ALERT_SLOTS]; int remove_count = 0; for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) { ship_objp = &Objects[so->objnum]; Assert(ship_objp->type == OBJ_SHIP); shipp = &Ships[ship_objp->instance]; if ( !(shipp->flags & SF_FROM_PLAYER_WING) && !(shipp->flags & SF_RED_ALERT_STORE_STATUS) ) { continue; } int found_match = 0; for ( i = 0; i < Red_alert_num_slots_used; i++ ) { ras = &Red_alert_wingman_status[i]; // we only want to restore ships which haven't been destroyed (which the RED_ALERT_EXITED_SHIP_CLASS identifies) if ( !stricmp(ras->name, shipp->ship_name) && (ras->ship_class != RED_ALERT_EXITED_SHIP_CLASS) ) { found_match = 1; // if necessary, restore correct ship class if ( ras->ship_class != shipp->ship_info_index ) { change_ship_type(SHIP_INDEX(shipp), ras->ship_class); } // restore hull and weapons ship_objp->hull_strength = ras->hull; red_alert_bash_weapons(ras, &shipp->weapons); red_alert_bash_subsys_status(ras, shipp); } } if ( !found_match ) { remove_list[remove_count++] = SHIP_INDEX(shipp); } } // remove ships for ( i = 0; i < remove_count; i++ ) { // remove ship red_alert_delete_ship(&Ships[remove_list[i]]); } } // write wingman status out to the specified file void red_alert_write_wingman_status(CFILE *fp) { int i, j; red_alert_ship_status *ras; cfwrite_int(Red_alert_num_slots_used, fp); if ( Red_alert_num_slots_used <= 0 ) { return; } Assert(strlen(Red_alert_precursor_mission) > 0 ); cfwrite_string(Red_alert_precursor_mission, fp); for ( i = 0; i < Red_alert_num_slots_used; i++ ) { ras = &Red_alert_wingman_status[i]; cfwrite_string(ras->name, fp); cfwrite_float(ras->hull, fp); cfwrite_string_len(Ship_info[ras->ship_class].name, fp); for ( j = 0; j < MAX_RED_ALERT_SUBSYSTEMS; j++ ) { cfwrite_float(ras->subsys_current_hits[j], fp); } for ( j = 0; j < SUBSYSTEM_MAX; j++ ) { cfwrite_float(ras->subsys_aggregate_current_hits[j], fp); } for ( j = 0; j < MAX_SHIP_WEAPONS; j++ ) { cfwrite_string_len( Weapon_info[ras->wep[j]].name, fp); cfwrite_int( ras->wep_count[j], fp ); } } } // same basic thing as the above but for writing in the campaign file - taylor void red_alert_write_wingman_status_campaign(CFILE *fp) { int i, j; red_alert_ship_status *ras; cfwrite_int(Red_alert_num_slots_used, fp); if ( Red_alert_num_slots_used <= 0 ) { return; } Assert(strlen(Red_alert_precursor_mission) > 0 ); cfwrite_string(Red_alert_precursor_mission, fp); for ( i = 0; i < Red_alert_num_slots_used; i++ ) { ras = &Red_alert_wingman_status[i]; cfwrite_string(ras->name, fp); cfwrite_float(ras->hull, fp); cfwrite_int(ras->ship_class, fp); for ( j = 0; j < MAX_RED_ALERT_SUBSYSTEMS; j++ ) { cfwrite_float(ras->subsys_current_hits[j], fp); } for ( j = 0; j < SUBSYSTEM_MAX; j++ ) { cfwrite_float(ras->subsys_aggregate_current_hits[j], fp); } for ( j = 0; j < MAX_SHIP_WEAPONS; j++ ) { cfwrite_int( ras->wep[j], fp ) ; cfwrite_int( ras->wep_count[j], fp ); } } } // red wingman status out of the specified file void red_alert_read_wingman_status(CFILE *fp, int version) { int i, j; red_alert_ship_status *ras; char tname[NAME_LENGTH]; Red_alert_num_slots_used = cfread_int(fp); if ( Red_alert_num_slots_used <= 0 ) { return; } cfread_string(Red_alert_precursor_mission, MAX_FILENAME_LEN, fp); for ( i = 0; i < Red_alert_num_slots_used; i++ ) { ras = &Red_alert_wingman_status[i]; cfread_string(ras->name, NAME_LENGTH, fp); ras->hull = cfread_float(fp); if (version >= 142) { cfread_string_len( tname, NAME_LENGTH, fp ); ras->ship_class = ship_info_lookup(tname); } else { ras->ship_class = cfread_int(fp); } for ( j = 0; j < MAX_RED_ALERT_SUBSYSTEMS; j++ ) { ras->subsys_current_hits[j] = cfread_float(fp); } for ( j = 0; j < SUBSYSTEM_MAX; j++ ) { ras->subsys_aggregate_current_hits[j] = cfread_float(fp); } for ( j = 0; j < MAX_SHIP_WEAPONS; j++ ) { if (version >= 142) { cfread_string_len( tname, NAME_LENGTH, fp ); ras->wep[j] = weapon_info_lookup(tname); } else { ras->wep[j] = cfread_int(fp) ; } ras->wep_count[j] = cfread_int(fp); } } } // same basic thing as the above but for read from the campaign file - taylor void red_alert_read_wingman_status_campaign(CFILE *fp, char ships[][NAME_LENGTH], char weapons[][NAME_LENGTH]) { int i, j; red_alert_ship_status *ras; int ras_tmp = -1; Red_alert_num_slots_used = cfread_int(fp); if ( Red_alert_num_slots_used <= 0 ) { return; } Assert( (ships != NULL) && (weapons != NULL) ); if ( (ships == NULL) || (weapons == NULL) ) return; cfread_string(Red_alert_precursor_mission, MAX_FILENAME_LEN, fp); for ( i = 0; i < Red_alert_num_slots_used; i++ ) { ras = &Red_alert_wingman_status[i]; cfread_string(ras->name, NAME_LENGTH, fp); ras->hull = cfread_float(fp); ras_tmp = cfread_int(fp); ras->ship_class = ship_info_lookup( ships[ras_tmp] ); for ( j = 0; j < MAX_RED_ALERT_SUBSYSTEMS; j++ ) { ras->subsys_current_hits[j] = cfread_float(fp); } for ( j = 0; j < SUBSYSTEM_MAX; j++ ) { ras->subsys_aggregate_current_hits[j] = cfread_float(fp); } for ( j = 0; j < MAX_SHIP_WEAPONS; j++ ) { ras_tmp = cfread_int(fp) ; ras->wep[j] = weapon_info_lookup( weapons[ras_tmp] ); ras->wep_count[j] = cfread_int(fp); } } } // return !0 if this is a red alert mission, otherwise return 0 int red_alert_mission() { return (The_mission.flags & MISSION_FLAG_RED_ALERT); } // called from sexpression code to start a red alert mission void red_alert_start_mission() { // if we are not in campaign mode, go to debriefing // if ( !(Game_mode & GM_CAMPAIGN_MODE) ) { // gameseq_post_event( GS_EVENT_DEBRIEF ); // proceed to debriefing // return; // } // check player health here. // if we're dead (or about to die), don't start red alert mission. if (Player_obj->type == OBJ_SHIP) { if (Player_obj->hull_strength > 0) { // make sure we don't die Player_ship->ship_guardian_threshold = SHIP_GUARDIAN_THRESHOLD_DEFAULT; // do normal red alert stuff Red_alert_new_mission_timestamp = timestamp(RED_ALERT_WARN_TIME); // throw down a sound here to make the warning seem ultra-important // gamesnd_play_iface(SND_USER_SELECT); snd_play(&(Snds[SND_DIRECTIVE_COMPLETE])); } } } // called from main game loop to check to see if we should move to a red alert mission int red_alert_check_status() { // if the timestamp is invalid, do nothing. if ( !timestamp_valid(Red_alert_new_mission_timestamp) ) return 0; // return if the timestamp hasn't elapsed yet if ( timestamp_elapsed(Red_alert_new_mission_timestamp) ) { // basic premise here is to stop the current mission, and then set the next mission in the campaign // which better be a red alert mission if ( Game_mode & GM_CAMPAIGN_MODE ) { red_alert_store_wingman_status(); mission_goal_fail_incomplete(); mission_campaign_store_goals_and_events_and_variables(); scoring_level_close(); mission_campaign_eval_next_mission(); mission_campaign_mission_over(); // CD CHECK gameseq_post_event(GS_EVENT_START_GAME); } else { gameseq_post_event(GS_EVENT_END_GAME); } } return 1; }