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/MenuUI/ReadyRoom.cpp $
15 * Ready Room code, which is the UI screen for selecting Campaign/mission to play next mainly.
18 * Revision 1.9 2005/08/12 09:01:11 taylor
19 * fix strange issue where campaign and single missions could end up mixed together in the same list
21 * Revision 1.8 2005/03/29 02:18:47 taylor
22 * Various 64-bit platform fixes
23 * Fix compiler errors with MAKE_FS1 and fix gr_set_bitmap() too
24 * Make sure that turrets can fire at asteroids for FS1 (needed for a couple missions)
25 * Streaming audio support (big thanks to Pierre Willenbrock!!)
26 * Removed dependance on strings.tbl for FS1 since we don't actually need it now
28 * Revision 1.7 2004/12/15 04:10:45 taylor
29 * outwnd_unix.cpp from fs2_open for logging to file in debug mode
30 * fixes for default function values
31 * always use vm_* functions for sanity sake
32 * make cfilearchiver 64-bit compatible
33 * fix crash on exit from double free()
34 * fix crash on startup from extra long GL extension string in debug
36 * Revision 1.6 2004/09/20 01:31:44 theoddone33
39 * Revision 1.5 2004/07/04 11:39:06 taylor
40 * fix missing debrief text, crash on exit, path separator's, warning fixes, no GR_SOFT
42 * Revision 1.4 2003/05/25 02:30:42 taylor
45 * Revision 1.3 2002/06/09 04:41:22 relnev
46 * added copyright header
48 * Revision 1.2 2002/06/02 04:26:34 relnev
51 * Revision 1.1.1.1 2002/05/03 03:28:09 root
55 * 20 9/30/99 5:59p Jefff
56 * fixed compile error in OEM ver
58 * 19 9/08/99 12:28p Jefff
60 * 18 9/06/99 6:38p Dave
61 * Improved CD detection code.
63 * 17 9/02/99 2:33p Jefff
64 * column heading coordinate changes
66 * 16 8/22/99 4:16p Jefff
67 * scroll button coord fixes
69 * 15 7/20/99 1:49p Dave
70 * Peter Drake build. Fixed some release build warnings.
72 * 14 7/19/99 2:13p Dave
73 * Added some new strings for Heiko.
75 * 13 7/15/99 9:20a Andsager
76 * FS2_DEMO initial checkin
78 * 12 7/09/99 5:54p Dave
79 * Seperated cruiser types into individual types. Added tons of new
80 * briefing icons. Campaign screen.
82 * 11 2/01/99 5:55p Dave
83 * Removed the idea of explicit bitmaps for buttons. Fixed text
84 * highlighting for disabled gadgets.
86 * 10 1/30/99 9:01p Dave
89 * 9 1/30/99 5:08p Dave
90 * More new hi-res stuff.Support for nice D3D textures.
92 * 8 1/29/99 12:47a Dave
93 * Put in sounds for beam weapon. A bunch of interface screens (tech
96 * 7 12/11/98 4:35p Andsager
97 * Fix sim room bug when no standalone missions
99 * 6 12/07/98 5:02p Dan
100 * Removed improper use of bitmap filename with extension,
102 * 5 10/13/98 9:28a Dave
103 * Started neatening up freespace.h. Many variables renamed and
104 * reorganized. Added AlphaColors.[h,cpp]
118 #include "gamesequence.h"
119 #include "missioncampaign.h"
122 #include "missionscreencommon.h"
123 #include "freespace.h"
124 #include "missionparse.h"
126 #include "managepilot.h"
128 #include "contexthelp.h"
129 #include "cfilesystem.h"
130 #include "freespace.h"
131 #include "alphacolors.h"
133 #define MAX_MISSIONS 1024
135 int Mission_list_coords[GR_NUM_RESOLUTIONS][4] = {
148 int Campaign_list_coords[GR_NUM_RESOLUTIONS][4] = {
162 // x coordinate offsets for data when campaign tab active
164 #define C_SUBTEXT_X 19
166 // x coordinate offsets for data when mission tab active
169 #define MODE_CAMPAIGNS 0
170 #define MODE_MISSIONS 1
172 #define MAX_LINES 200
173 #define MAX_DESC_LINES 200
174 #define NUM_BUTTONS 11
175 #define LIST_BUTTONS_MAX 40
177 #define SCROLL_UP_BUTTON 0
178 #define SCROLL_DOWN_BUTTON 1
179 #define MISSION_TAB 2
180 #define CAMPAIGN_TAB 3
181 #define HELP_BUTTON 4
182 #define COMMIT_BUTTON 5
183 #define OPTIONS_BUTTON 6
184 #define TECH_DATABASE_BUTTON 7
185 #define SIMULATOR_BUTTON 8
186 #define CUTSCENES_BUTTON 9
187 #define CREDITS_BUTTON 10
189 #define CAMPAIGN_MISSION_HASH_SIZE 307
191 struct sim_room_buttons {
192 const char *filename;
195 UI_BUTTON button; // because we have a class inside this struct, we need the constructor below..
197 sim_room_buttons(const char *name, int x1, int y1, int xt1, int yt1, int h) : filename(name), x(x1), y(y1), xt(xt1), yt(yt1), hotspot(h) {}
200 static sim_room_buttons Buttons[GR_NUM_RESOLUTIONS][NUM_BUTTONS] = {
204 sim_room_buttons("LMB_04", 7, 135, -1, -1, 4), // up arrows
205 sim_room_buttons("LMB_05", 7, 182, -1, -1, 5), // down arrows
206 sim_room_buttons("LMB_06", 23, 396, -1, -1, 6), // single missions
207 sim_room_buttons("LMB_07", 198, 396, -1, -1, 7), // campaign missions
208 sim_room_buttons("LMB_08", 469, 427, -1, -1, 8), // help
209 sim_room_buttons("LMB_09", 554, 411, -1, -1, 9), // commit
210 sim_room_buttons("LMB_10", 447, 452, -1, -1, 10), // options
212 sim_room_buttons("TDB_00", 0, 0, -1, -1, 0), // technical database
213 sim_room_buttons("TDB_01", 0, 19, -1, -1, 1), // mission simulator
214 sim_room_buttons("TDB_02", 0, 35, -1, -1, 2), // cutscenes
215 sim_room_buttons("TDB_03", 0, 56, -1, -1, 3), // credits
217 sim_room_buttons("LMB_04", 1, 99, -1, -1, 4),
218 sim_room_buttons("LMB_05", 1, 381, -1, -1, 5),
219 sim_room_buttons("LMB_06", 6, 438, 40, 445, 6),
220 sim_room_buttons("LMB_07", 6, 457, 40, 462, 7),
221 sim_room_buttons("LMB_08", 534, 426, 500, 440, 8),
222 sim_room_buttons("LMB_09", 571, 426, 572, 413, 9),
223 sim_room_buttons("LMB_10", 534, 455, 480, 462, 10),
225 sim_room_buttons("TDB_00", 7, 3, 37, 7, 0),
226 sim_room_buttons("TDB_01", 7, 18, 37, 23, 1),
227 sim_room_buttons("TDB_02", 7, 34, 37, 38, 2),
228 sim_room_buttons("TDB_03", 7, 49, 37, 54, 3),
232 sim_room_buttons("2_LMB_04", 2, 159, -1, -1, 4),
233 sim_room_buttons("2_LMB_05", 2, 609, -1, -1, 5),
234 sim_room_buttons("2_LMB_06", 10, 701, 64, 712, 6),
235 sim_room_buttons("2_LMB_07", 10, 732, 64, 739, 7),
236 sim_room_buttons("2_LMB_08", 854, 681, 800, 704, 8),
237 sim_room_buttons("2_LMB_09", 914, 681, 915, 660, 9),
238 sim_room_buttons("2_LMB_10", 854, 728, 800, 728, 10),
240 sim_room_buttons("2_TDB_00", 12, 5, 59, 12, 0),
241 sim_room_buttons("2_TDB_01", 12, 31, 59, 37, 1),
242 sim_room_buttons("2_TDB_02", 12, 56, 59, 62, 2),
243 sim_room_buttons("2_TDB_03", 12, 81, 59, 88, 3),
248 const char *Sim_filename[GR_NUM_RESOLUTIONS] = {
252 const char *Sim_mask_filename[GR_NUM_RESOLUTIONS] = {
257 const char *Campaign_filename[GR_NUM_RESOLUTIONS] = {
261 const char *Campaign_mask_filename[GR_NUM_RESOLUTIONS] = {
266 // misc text. ("Mission" and "Filename"
268 #define NUM_SIM_MISC_TEXT 2
269 #define SIM_MISC_TEXT_MISSION 0
270 #define SIM_MISC_TEXT_FILENAME 1
271 int Sim_misc_text_coords[GR_NUM_RESOLUTIONS][NUM_SIM_MISC_TEXT][2] = {
283 // readyroom text line stuff
284 #define READYROOM_LINE_CAMPAIGN 1
285 #define READYROOM_LINE_CMISSION 2
286 #define READYROOM_LINE_MISSION 3
288 #define READYROOM_FLAG_FROM_VOLITION (1<<0) // volition made
290 #define READYROOM_FLAG_FROM_MDISK (1<<1) // mission disk
293 int type; // see READYROOM_LINE_* defines above
295 const char *filename;
296 int x; // X coordinate of line
297 int y; // Y coordinate of line
298 int flags; // special flags, see READYROOM_FLAG_* defines above
299 } sim_room_lines[MAX_LINES];
301 static char Cur_campaign[MAX_FILENAME_LEN];
302 static char *Mission_filenames[MAX_MISSIONS];
303 static char *Standalone_mission_names[MAX_MISSIONS];
304 static int Standalone_mission_flags[MAX_MISSIONS];
305 static char *Campaign_missions[MAX_CAMPAIGN_MISSIONS];
306 static char *Campaign_mission_names[MAX_CAMPAIGN_MISSIONS];
307 static int Campaign_mission_flags[MAX_MISSIONS];
308 static char *Campaign_descs[MAX_CAMPAIGNS];
309 static char *Campaign_descs_temp[MAX_CAMPAIGNS];
310 static char *Campaign_file_names_temp[MAX_CAMPAIGNS];
311 static int Standalone_mission_names_inited = 0;
312 static int Campaign_names_inited = 0;
313 static int Campaign_mission_names_inited = 0;
314 static int Num_standalone_missions;
315 static int Num_campaign_missions;
316 static int Scroll_offset;
317 static int Selected_line;
318 static int Num_lines;
319 static int Num_campaign_missions_with_info = 0;
320 static int Num_standalone_missions_with_info = 0;
327 static int Background_bitmap;
328 static UI_WINDOW Ui_window;
329 static UI_BUTTON List_buttons[LIST_BUTTONS_MAX]; // buttons for each line of text in list
331 typedef struct hash_node {
333 const char *filename;
336 static hash_node *Campaign_mission_hash_table[CAMPAIGN_MISSION_HASH_SIZE];
337 static int Hash_table_inited;
339 // special icons (1.04 + stuff)
341 #define NUM_MISSION_ICONS 2
343 #define NUM_MISSION_ICONS 1
345 #define MISSION_ICON_VOLITION 0 // mini volition death's head :)
347 #define MISSION_ICON_MDISK 1 // Silent Threat mini icon
350 // icon offsets (see LIST_ defines above
351 //#define MISSION_ICON_VOLITION_X (46)
352 #define MISSION_ICON_VOLITION_Y_OFFSET (-1)
354 #define MISSION_ICON_MDISK_Y_OFFSET (0)
358 static int Sim_volition_icon_x[GR_NUM_RESOLUTIONS] = {
368 static int Sim_silent_icon_x[GR_NUM_RESOLUTIONS] = {
374 // special icons themselves
375 int Mission_icon_bitmaps[NUM_MISSION_ICONS];
377 const char *Mission_icon_bitmap_filenames[NUM_MISSION_ICONS] = {
386 void sim_room_load_mission_icons();
387 void sim_room_unload_mission_icons();
388 void sim_room_blit_icons(int line_index, int y_start, fs_builtin_mission *fb = NULL, int is_md = 0);
390 // Finds a hash value for mission filename
392 // returns hash value
393 int hash_filename(const char *filename) {
395 const char *ptr = filename;
397 // Dont hash .fsm extension, convert all to upper case
398 for (int i=0; i < ((signed int)(strlen(filename)) - 4); i++) {
399 hash_val = (hash_val << 4) + toupper(*ptr++);
402 return int(hash_val % CAMPAIGN_MISSION_HASH_SIZE);
405 // insert filename into Campaign_mission_hash_table
407 // returns 1 if successful, 0 if could not allocate memory
408 int hash_insert(const char *filename) {
409 int hash_val = hash_filename(filename);
412 // Check if table empty
413 if (Campaign_mission_hash_table[hash_val] == NULL) {
414 Campaign_mission_hash_table[hash_val] = new hash_node;
416 cur_node = Campaign_mission_hash_table[hash_val];
418 if (cur_node == NULL) {
419 // Unable to allocate memory
423 // Walk down list to first empty node
424 cur_node = Campaign_mission_hash_table[hash_val];
425 while (cur_node->next != NULL) {
426 cur_node = cur_node->next;
430 cur_node->next = new hash_node;
432 if (cur_node->next == NULL) {
433 // unable to allocate memory
436 cur_node = cur_node->next;
441 cur_node->next = NULL;
442 cur_node->filename = filename;
448 // Checks if a filename already exitst in the hash table
450 // returns 1 if found (collision), 0 if no collision
451 int campaign_mission_hash_collision(const char *filename)
453 int hash_val = hash_filename(filename);
454 hash_node *cur_node = Campaign_mission_hash_table[hash_val];
456 if (cur_node == NULL) {
461 if (!SDL_strcasecmp(filename, cur_node->filename)) {
465 cur_node = cur_node->next;
466 } while (cur_node != NULL);
468 // Ran out of stuff to check
472 // builds hash table of campaign mission filenames
474 // returns 1 if successful, 0 if not successful
475 int build_campaign_mission_filename_hash_table()
478 // Go through all campaign missions
479 for (int i=0; i<Num_campaign_missions; i++) {
480 rval = hash_insert(Campaign_missions[i]);
490 // deletes hash table nodes
492 void campaign_mission_hash_table_delete()
496 for (int i=0; i<CAMPAIGN_MISSION_HASH_SIZE; i++) {
497 // Look for entries into array
498 if (Campaign_mission_hash_table[i] != NULL) {
499 cur_node = Campaign_mission_hash_table[i];
501 // Walk down the list deleting self
502 while (cur_node->next != NULL) {
503 hash_node *temp = cur_node->next;
510 Campaign_mission_hash_table[i] = NULL;
516 // add a line of sim_room smuck to end of list
517 int sim_room_line_add(int type, char *name, char *filename, int x, int y, int flags)
519 if (Num_lines >= MAX_LINES)
522 sim_room_lines[Num_lines].type = type;
523 sim_room_lines[Num_lines].name = name;
524 sim_room_lines[Num_lines].filename = filename;
525 sim_room_lines[Num_lines].x = x;
526 sim_room_lines[Num_lines].y = y;
527 sim_room_lines[Num_lines].flags = flags;
531 // filter out all multiplayer campaigns
532 int campaign_room_campaign_filter(const char *filename)
534 int type, max_players;
535 char name[NAME_LENGTH], *desc = NULL;
538 // also need to check if this is the builtin campaign
539 if ( game_find_builtin_mission(filename) && mission_campaign_get_info(filename, name, &type, &max_players, &desc) ) {
541 if ( mission_campaign_get_info(filename, name, &type, &max_players, &desc) ) {
543 if ( type == CAMPAIGN_TYPE_SINGLE ) {
544 Campaign_file_names_temp[Num_campaigns] = strdup(filename);
545 Campaign_descs_temp[Num_campaigns++] = desc;
557 // build up a list of all missions in all campaigns.
558 int sim_room_campaign_mission_filter(const char *filename)
562 num = mission_campaign_get_mission_list(filename, &Campaign_missions[Num_campaign_missions], MAX_CAMPAIGN_MISSIONS - Num_campaign_missions);
566 Num_campaign_missions += num;
570 // filter out all missions already used in existing campaigns
571 int sim_room_standalone_mission_filter(const char *filename)
574 char mission_name[255];
576 // Check if a campaign mission (single and multi)
577 if (campaign_mission_hash_collision(filename)) {
581 // Check if a standalone multi mission OR Mdisk mission with data
582 type = mission_parse_is_multi(filename, mission_name);
583 if (type && !(type & MISSION_TYPE_SINGLE))
589 // builds up list of standalone missions and adds them to missions simulator
590 // processes one mission per frame
592 // returns 1 if finished with all missions, 0 otherwise
594 int build_standalone_mission_list_do_frame()
596 int font_height = gr_get_font_height();
597 char filename[MAX_FILENAME_LEN];
600 // When no standalone missions in data directory
601 if (Num_standalone_missions == 0) {
602 Standalone_mission_names_inited = 1;
606 // Set global variable so we we'll have list available next time
607 Standalone_mission_names[Num_standalone_missions_with_info] = NULL;
608 Standalone_mission_flags[Num_standalone_missions_with_info] = 0;
610 if (Num_standalone_missions > 0) { // sanity check
611 if (strlen(Mission_filenames[Num_standalone_missions_with_info]) < MAX_FILENAME_LEN - 4) { // sanity check?
612 SDL_strlcpy(filename, Mission_filenames[Num_standalone_missions_with_info], SDL_arraysize(filename));
615 SDL_snprintf(str, SDL_arraysize(str), XSTR("Single Mission\n\n%s",989), filename);
616 popup_change_text(str);
618 // tack on an extension
619 SDL_strlcat(filename, FS_MISSION_FILE_EXT, SDL_arraysize(filename));
620 if (!get_mission_info(filename)) {
621 Standalone_mission_names[Num_standalone_missions_with_info] = strdup(The_mission.name);
622 Standalone_mission_flags[Num_standalone_missions_with_info] = The_mission.game_type;
623 int y = Num_lines * (font_height + 2);
625 // determine some extra information
627 fs_builtin_mission *fb = game_find_builtin_mission(filename);
628 if((fb != NULL) && (fb->flags & FSB_FROM_VOLITION)){
629 flags |= READYROOM_FLAG_FROM_VOLITION;
632 if((fb != NULL) && (fb->flags & FSB_FROM_MDISK)){
633 flags |= READYROOM_FLAG_FROM_MDISK;
638 sim_room_line_add(READYROOM_LINE_MISSION, Standalone_mission_names[Num_standalone_missions_with_info], Mission_filenames[Num_standalone_missions_with_info], list_x1 + M_TEXT_X, y, flags);
642 Num_standalone_missions_with_info++;
645 if (Num_standalone_missions_with_info == Num_standalone_missions) {
646 Standalone_mission_names_inited = 1;
653 // builds up list of already played missions in a campaign and adds them to missions simulator
654 // processes one mission per frame
656 // returns 1 if finished with all missions, 0 otherwise
658 int build_campaign_mission_list_do_frame()
660 int font_height = gr_get_font_height();
663 // When no campaign files in data directory
664 if (Campaign.num_missions == 0) {
665 Campaign_mission_names_inited = 1;
670 SDL_snprintf(str, SDL_arraysize(str), XSTR("Campaign Mission\n\n%s",990), Campaign.missions[Num_campaign_missions_with_info].name);
671 popup_change_text(str);
673 // Set global variable so we we'll have list available next time
674 Campaign_mission_names[Num_campaign_missions_with_info] = NULL;
675 Campaign_mission_flags[Num_campaign_missions_with_info] = 0;
677 // Only allow missions already completed
678 if (Campaign.missions[Num_campaign_missions_with_info].completed) {
679 if (!get_mission_info(Campaign.missions[Num_campaign_missions_with_info].name)) {
681 Campaign_mission_names[Num_campaign_missions_with_info] = strdup(The_mission.name);
682 Campaign_mission_flags[Num_campaign_missions_with_info] = The_mission.game_type;
683 int y = Num_campaign_missions_with_info * (font_height + 2);
685 // determine some extra information
687 fs_builtin_mission *fb = game_find_builtin_mission(Campaign.missions[Num_campaign_missions_with_info].name);
688 if((fb != NULL) && (fb->flags & FSB_FROM_VOLITION)){
689 flags |= READYROOM_FLAG_FROM_VOLITION;
693 if((fb != NULL) && (fb->flags & FSB_FROM_MDISK)){
694 flags |= READYROOM_FLAG_FROM_MDISK;
698 sim_room_line_add(READYROOM_LINE_CMISSION, Campaign_mission_names[Num_campaign_missions_with_info], Campaign.missions[Num_campaign_missions_with_info].name, list_x1 + C_SUBTEXT_X, y, flags);
702 Num_campaign_missions_with_info++;
704 if (Num_campaign_missions_with_info == Campaign.num_missions) {
705 Campaign_mission_names_inited = 1;
712 // Builds list of either (1) standalone missions or (2) already played missions in current campaign
713 // First time through, it builds list, next time it uses precompiled list
714 void sim_room_build_listing()
717 int font_height = gr_get_font_height();
718 char full_filename[256];
721 list_y = Mission_list_coords[gr_screen.res][1];
722 list_h = Mission_list_coords[gr_screen.res][3];
724 // Stand alone single player missions.
725 if (Player->readyroom_listing_mode == MODE_MISSIONS) {
726 if (Hash_table_inited) {
727 if (!Standalone_mission_names_inited) { // Is this the first time through
728 // build_list_do_frame builds list and adds sim room line and sets Standalone_mission_names_inited
729 popup_till_condition(build_standalone_mission_list_do_frame, POPUP_CANCEL, XSTR("Loading missions", 991) );
731 for (i=0; i<Num_standalone_missions_with_info; i++) {
732 if (Standalone_mission_names[i]) {
733 // determine some extra information
735 memset(full_filename, 0, 256);
736 SDL_strlcpy(full_filename, cf_add_ext(Mission_filenames[i], FS_MISSION_FILE_EXT), SDL_arraysize(full_filename));
737 fs_builtin_mission *fb = game_find_builtin_mission(full_filename);
738 if((fb != NULL) && (fb->flags & FSB_FROM_VOLITION)){
739 flags |= READYROOM_FLAG_FROM_VOLITION;
743 if((fb != NULL) && (fb->flags & FSB_FROM_MDISK)){
744 flags |= READYROOM_FLAG_FROM_MDISK;
748 sim_room_line_add(READYROOM_LINE_MISSION, Standalone_mission_names[i], Mission_filenames[i], list_x1 + M_TEXT_X, y, flags);
749 y += font_height + 2;
756 list_y += font_height + 2;
757 list_h -= font_height - 2;
759 if (!Campaign_mission_names_inited) { // Is this the first time through
760 popup_till_condition(build_campaign_mission_list_do_frame, POPUP_CANCEL, XSTR("Loading campaign missions",992));
761 // builds list, adds sim room line and sets Campaign_mission_names_inited
763 for (i=0; i<Num_campaign_missions_with_info; i++) {
764 if (Campaign_mission_names[i]) {
765 // determine some extra information
767 memset(full_filename, 0, 256);
768 SDL_strlcpy(full_filename, cf_add_ext(Campaign.missions[i].name, FS_MISSION_FILE_EXT), SDL_arraysize(full_filename));
769 fs_builtin_mission *fb = game_find_builtin_mission(full_filename);
770 if((fb != NULL) && (fb->flags & FSB_FROM_VOLITION)){
771 flags |= READYROOM_FLAG_FROM_VOLITION;
775 if((fb != NULL) && (fb->flags & FSB_FROM_MDISK)){
776 flags |= READYROOM_FLAG_FROM_MDISK;
780 sim_room_line_add(READYROOM_LINE_CMISSION, Campaign_mission_names[i], Campaign.missions[i].name, list_x1 + C_SUBTEXT_X, y, flags);
788 int sim_room_line_query_visible(int n)
792 if ((n < 0) || (n >= Num_lines))
795 y = sim_room_lines[n].y - sim_room_lines[Scroll_offset].y;
796 if ((y < 0) || (y + gr_get_font_height() > list_h))
802 void sim_room_scroll_screen_up()
804 if (Player->readyroom_listing_mode != MODE_MISSIONS) {
807 gamesnd_play_iface(SND_SCROLL);
810 gamesnd_play_iface(SND_GENERAL_FAIL);
817 SDL_assert(Selected_line > Scroll_offset);
818 while (!sim_room_line_query_visible(Selected_line))
821 gamesnd_play_iface(SND_SCROLL);
824 gamesnd_play_iface(SND_GENERAL_FAIL);
827 void sim_room_scroll_line_up()
831 if (Selected_line < Scroll_offset)
832 Scroll_offset = Selected_line;
834 gamesnd_play_iface(SND_SCROLL);
837 gamesnd_play_iface(SND_GENERAL_FAIL);
840 void sim_room_scroll_screen_down()
842 if (Player->readyroom_listing_mode != MODE_MISSIONS) {
843 if (sim_room_lines[Num_lines - 1].y + gr_get_font_height() > sim_room_lines[Scroll_offset].y + list_h) {
845 gamesnd_play_iface(SND_SCROLL);
848 gamesnd_play_iface(SND_GENERAL_FAIL);
853 if (sim_room_lines[Num_lines - 1].y + gr_get_font_height() > sim_room_lines[Scroll_offset].y + list_h) {
855 while (!sim_room_line_query_visible(Selected_line)) {
857 SDL_assert(Selected_line < Num_lines);
860 gamesnd_play_iface(SND_SCROLL);
863 gamesnd_play_iface(SND_GENERAL_FAIL);
866 void sim_room_scroll_line_down()
868 if (Selected_line < Num_lines - 1) {
871 SDL_assert(Selected_line > Scroll_offset);
872 while (!sim_room_line_query_visible(Selected_line))
875 gamesnd_play_iface(SND_SCROLL);
878 gamesnd_play_iface(SND_GENERAL_FAIL);
882 // returns: 0 = success, !0 = aborted or failed
883 int ready_room_reset_campaign()
887 z = popup(PF_TITLE_BIG | PF_TITLE_RED, 2, POPUP_CANCEL, POPUP_OK, XSTR( "Warning\nThis will cause all progress in your\ncurrent campaign to be lost", 110) );
890 mission_campaign_savefile_delete(Campaign.filename);
891 mission_campaign_load(Campaign.filename);
899 // Decide if we should offer choice to resume this savegame
900 int sim_room_can_resume_savegame(char *savegame_filename)
902 #ifdef FREESPACE_SAVERESTORE_SYSTEM
903 char savegame_mission[MAX_FILENAME_LEN];
905 if (state_read_description(savegame_filename, NULL, savegame_mission)) {
909 if (SDL_strcasecmp(Game_current_mission_filename, savegame_mission)) {
919 // Decide wether to resume a save game or not
920 // exit: 1 => savegame has been restored
921 // 0 => no restore, proceed to briefing
922 // -1 => don't start mission at all
923 int sim_room_maybe_resume_savegame()
925 // MWA -- 3/26/98 -- removed all savegame references in game
929 char savegame_filename[_MAX_FNAME];
930 int popup_rval = -1, resume_savegame = 0;
932 // Generate the save-game filename for this campaign
933 memset(savegame_filename, 0, _MAX_FNAME);
934 mission_campaign_savefile_generate_root(savegame_filename);
935 strcat(savegame_filename, NOX("svg"));
937 // Decide if we should offer choice to resume this savegame
938 if ( sim_room_can_resume_savegame(savegame_filename) ) {
939 popup_rval = popup(0, 3, XSTR("&Cancel",-1), XSTR("&Overwrite",-1), XSTR("&Resume",-1), XSTR("A save game for this mission exists.", -1));
940 switch ( popup_rval ) {
943 resume_savegame = -1;
953 resume_savegame = -1;
960 if (resume_savegame == 1) {
961 if ( state_restore_all(savegame_filename) == -1 ) {
962 popup_rval = popup(PF_TITLE_BIG | PF_TITLE_RED, 2, POPUP_NO, POPUP_YES, XSTR("Error\nSaved misison could not be loaded.\nDo you wish to start this mission from the beginning?", -1));
963 if (popup_rval == 1) {
966 resume_savegame = -1;
974 // If we are resuming this savegame, then delete the file
975 if (resume_savegame == 1) {
976 cf_delete(savegame_filename);
979 return resume_savegame;
983 #if defined(FS2_DEMO) || defined(FS1_DEMO)
984 static void continue_campaign_callback(int choice)
989 mission_campaign_savefile_delete(Campaign.filename);
990 mission_campaign_load(Campaign.filename);
991 mission_campaign_next_mission();
993 // set the bit for campaign mode
994 Game_mode |= GM_CAMPAIGN_MODE;
995 gameseq_post_event( GS_EVENT_START_GAME );
1000 void readyroom_continue_campaign()
1002 if (mission_campaign_next_mission()) { // is campaign and next mission valid?
1003 #if defined(FS2_DEMO) || defined(FS1_DEMO)
1004 popup_callback(continue_campaign_callback, PF_BODY_BIG, 2, POPUP_NO, POPUP_YES, XSTR( "Demo Campaign Is Over. Would you like to play the campaign again?", 111) );
1006 gamesnd_play_iface(SND_GENERAL_FAIL);
1009 // make it the bottom button so that the graphic looks right
1010 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR( "The campaign is over. To replay the campaign, either create a new pilot or restart the campaign in the campaign room.", 112) );
1012 popup(0, 1, POPUP_OK, XSTR( "The campaign is over. To replay the campaign, either create a new pilot or restart the campaign in the campaign room.", 112) );
1019 // set the bit for campaign mode
1020 Game_mode |= GM_CAMPAIGN_MODE;
1021 gameseq_post_event( GS_EVENT_START_GAME );
1024 void sim_room_commit()
1026 if ((Selected_line >= Num_lines) || !sim_room_lines[Selected_line].filename) {
1027 gamesnd_play_iface(SND_GENERAL_FAIL);
1031 SDL_strlcpy(Game_current_mission_filename, sim_room_lines[Selected_line].filename, SDL_arraysize(Game_current_mission_filename));
1033 Game_mode &= ~(GM_CAMPAIGN_MODE); // be sure this bit is clear
1035 // don't resume savegame, proceed to briefing
1036 gameseq_post_event(GS_EVENT_START_GAME);
1037 gamesnd_play_iface(SND_COMMIT_PRESSED);
1040 int sim_room_button_pressed(int n)
1043 case SCROLL_UP_BUTTON:
1044 sim_room_scroll_screen_up();
1047 case SCROLL_DOWN_BUTTON:
1048 sim_room_scroll_screen_down();
1053 game_feature_not_in_demo_popup();
1054 // gamesnd_play_iface(SND_GENERAL_FAIL);
1057 Player->readyroom_listing_mode = MODE_MISSIONS;
1058 Selected_line = Scroll_offset = 0;
1059 gamesnd_play_iface(SND_USER_SELECT);
1060 sim_room_build_listing();
1065 Player->readyroom_listing_mode = MODE_CAMPAIGNS;
1067 gamesnd_play_iface(SND_USER_SELECT);
1068 sim_room_build_listing();
1076 launch_context_help();
1077 gamesnd_play_iface(SND_HELP_PRESSED);
1080 case OPTIONS_BUTTON:
1081 gamesnd_play_iface(SND_SWITCH_SCREENS);
1082 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
1085 case TECH_DATABASE_BUTTON:
1086 gamesnd_play_iface(SND_SWITCH_SCREENS);
1087 gameseq_post_event(GS_EVENT_TECH_MENU);
1090 case CUTSCENES_BUTTON:
1091 gamesnd_play_iface(SND_SWITCH_SCREENS);
1092 gameseq_post_event(GS_EVENT_GOTO_VIEW_CUTSCENES_SCREEN);
1095 case CREDITS_BUTTON:
1096 gamesnd_play_iface(SND_SWITCH_SCREENS);
1097 gameseq_post_event(GS_EVENT_CREDITS);
1104 // ---------------------------------------------------------------------
1105 // mission_sim_room_init()
1107 // Initialize the sim_room assignment screen system. Called when GS_STATE_sim_room_SCREEN
1110 void sim_room_init()
1113 sim_room_buttons *b;
1116 list_x1 = Mission_list_coords[gr_screen.res][0];
1117 list_x2 = Campaign_list_coords[gr_screen.res][0];
1118 list_y = Mission_list_coords[gr_screen.res][1];
1119 list_w1 = Mission_list_coords[gr_screen.res][2];
1120 list_w2 = Campaign_list_coords[gr_screen.res][2];
1121 list_h = Mission_list_coords[gr_screen.res][3];
1123 // force mode to valid range. I once had a bogus value here. Don't know how that happened, though.
1124 if (Player->readyroom_listing_mode != MODE_MISSIONS)
1125 Player->readyroom_listing_mode = MODE_CAMPAIGNS;
1127 *Game_current_mission_filename = 0;
1128 common_set_interface_palette("InterfacePalette"); // set the interface palette
1129 Ui_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0);
1130 Ui_window.set_mask_bmap(Sim_mask_filename[gr_screen.res]);
1132 for (i=0; i<NUM_BUTTONS; i++) {
1133 b = &Buttons[gr_screen.res][i];
1135 b->button.create(&Ui_window, "", b->x, b->y, 60, 30, (i < 2), 1);
1136 // set up callback for when a mouse first goes over a button
1137 b->button.set_highlight_action(common_play_highlight_sound);
1138 b->button.set_bmaps(b->filename);
1139 b->button.link_hotspot(b->hotspot);
1143 // screen/button specific text
1144 Ui_window.add_XSTR("Single Missions", 1060, Buttons[gr_screen.res][MISSION_TAB].xt, Buttons[gr_screen.res][MISSION_TAB].yt, &Buttons[gr_screen.res][MISSION_TAB].button, UI_XSTR_COLOR_GREEN);
1145 Ui_window.add_XSTR("Campaign Missions", 1061, Buttons[gr_screen.res][CAMPAIGN_TAB].xt, Buttons[gr_screen.res][CAMPAIGN_TAB].yt, &Buttons[gr_screen.res][CAMPAIGN_TAB].button, UI_XSTR_COLOR_GREEN);
1146 Ui_window.add_XSTR("Help", 928, Buttons[gr_screen.res][HELP_BUTTON].xt, Buttons[gr_screen.res][HELP_BUTTON].yt, &Buttons[gr_screen.res][HELP_BUTTON].button, UI_XSTR_COLOR_GREEN);
1147 Ui_window.add_XSTR("Commit", 1062, Buttons[gr_screen.res][COMMIT_BUTTON].xt, Buttons[gr_screen.res][COMMIT_BUTTON].yt, &Buttons[gr_screen.res][COMMIT_BUTTON].button, UI_XSTR_COLOR_PINK);
1148 Ui_window.add_XSTR("Options", 1036, Buttons[gr_screen.res][OPTIONS_BUTTON].xt, Buttons[gr_screen.res][OPTIONS_BUTTON].yt, &Buttons[gr_screen.res][OPTIONS_BUTTON].button, UI_XSTR_COLOR_GREEN);
1150 // common tab button text
1151 Ui_window.add_XSTR("Technical Database", 1055, Buttons[gr_screen.res][TECH_DATABASE_BUTTON].xt, Buttons[gr_screen.res][TECH_DATABASE_BUTTON].yt, &Buttons[gr_screen.res][TECH_DATABASE_BUTTON].button, UI_XSTR_COLOR_GREEN);
1152 Ui_window.add_XSTR("Mission Simulator", 1056, Buttons[gr_screen.res][SIMULATOR_BUTTON].xt, Buttons[gr_screen.res][SIMULATOR_BUTTON].yt, &Buttons[gr_screen.res][SIMULATOR_BUTTON].button, UI_XSTR_COLOR_GREEN);
1153 Ui_window.add_XSTR("Cutscenes", 1057, Buttons[gr_screen.res][CUTSCENES_BUTTON].xt, Buttons[gr_screen.res][CUTSCENES_BUTTON].yt, &Buttons[gr_screen.res][CUTSCENES_BUTTON].button, UI_XSTR_COLOR_GREEN);
1154 Ui_window.add_XSTR("Credits", 1058, Buttons[gr_screen.res][CREDITS_BUTTON].xt, Buttons[gr_screen.res][CREDITS_BUTTON].yt, &Buttons[gr_screen.res][CREDITS_BUTTON].button, UI_XSTR_COLOR_GREEN);
1156 // misc text - not associated with any buttons
1157 Ui_window.add_XSTR("Mission", 1063, Sim_misc_text_coords[gr_screen.res][SIM_MISC_TEXT_MISSION][0], Sim_misc_text_coords[gr_screen.res][SIM_MISC_TEXT_MISSION][1], NULL, UI_XSTR_COLOR_GREEN);
1158 Ui_window.add_XSTR("Filename", 1064, Sim_misc_text_coords[gr_screen.res][SIM_MISC_TEXT_FILENAME][0], Sim_misc_text_coords[gr_screen.res][SIM_MISC_TEXT_FILENAME][1], NULL, UI_XSTR_COLOR_GREEN);
1161 for (i=0; i<LIST_BUTTONS_MAX; i++) {
1162 List_buttons[i].create(&Ui_window, "", 0, 0, 60, 30, 0, 1);
1163 List_buttons[i].hide();
1164 List_buttons[i].disable();
1167 // set up sim_rooms for buttons so we draw the correct animation frame when a key is pressed
1168 Buttons[gr_screen.res][SCROLL_UP_BUTTON].button.set_hotkey(SDLK_PAGEUP);
1169 Buttons[gr_screen.res][SCROLL_DOWN_BUTTON].button.set_hotkey(SDLK_PAGEDOWN);
1170 Buttons[gr_screen.res][COMMIT_BUTTON].button.set_hotkey(KEY_CTRLED | SDLK_RETURN);
1172 Background_bitmap = bm_load(Sim_filename[gr_screen.res]);
1174 // load in help overlay bitmap
1175 help_overlay_load(SIM_ROOM_OVERLAY);
1176 help_overlay_set_state(SIM_ROOM_OVERLAY,0);
1178 Scroll_offset = Selected_line = 0;
1180 SDL_strlcpy(Cur_campaign, Player->current_campaign, SDL_arraysize(Cur_campaign));
1181 mission_load_up_campaign();
1182 mission_campaign_next_mission();
1184 Num_campaigns = Num_campaign_missions = 0;
1185 Get_file_list_filter = sim_room_campaign_mission_filter;
1186 SDL_snprintf(wild_card, SDL_arraysize(wild_card), "*%s", FS_CAMPAIGN_FILE_EXT);
1187 Num_campaigns = cf_get_file_list(MAX_CAMPAIGNS, Campaign_file_names, CF_TYPE_MISSIONS, wild_card, CF_SORT_NAME);
1189 Hash_table_inited = 0;
1190 if (build_campaign_mission_filename_hash_table()) {
1191 Hash_table_inited = 1;
1195 GR_MAYBE_CLEAR_RES(Background_bitmap);
1196 if(Background_bitmap != -1){
1197 gr_set_bitmap(Background_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1203 Get_file_list_filter = sim_room_standalone_mission_filter;
1204 SDL_snprintf(wild_card, SDL_arraysize(wild_card), "*%s", FS_MISSION_FILE_EXT);
1205 Num_standalone_missions = cf_get_file_list(MAX_MISSIONS, Mission_filenames, CF_TYPE_MISSIONS, wild_card, CF_SORT_NAME);
1207 Num_campaign_missions_with_info = Num_standalone_missions_with_info = Standalone_mission_names_inited = Campaign_names_inited = Campaign_mission_names_inited = 0;
1208 sim_room_build_listing();
1210 // load special mission icons
1211 sim_room_load_mission_icons();
1214 // ---------------------------------------------------------------------
1217 // Cleanup the sim_room assignment screen system. Called when GS_STATE_sim_room_SCREEN
1220 void sim_room_close()
1224 for (i=0; i<Num_campaign_missions; i++) {
1225 if (Campaign_missions[i] != NULL) {
1226 free(Campaign_missions[i]);
1227 Campaign_missions[i] = NULL;
1231 if (Background_bitmap >= 0)
1232 bm_unload(Background_bitmap);
1234 if (Standalone_mission_names_inited){
1235 for (i=0; i<Num_standalone_missions; i++){
1236 if (Standalone_mission_names[i] != NULL) {
1237 free(Standalone_mission_names[i]);
1238 Standalone_mission_names[i] = NULL;
1240 Standalone_mission_flags[i] = 0;
1244 if (Campaign_names_inited) {
1245 for (i=0; i<Num_campaigns; i++) {
1246 if (Campaign_names[i] != NULL) {
1247 free(Campaign_names[i]);
1248 Campaign_names[i] = NULL;
1253 if (Campaign_mission_names_inited) {
1254 for (i=0; i<Campaign.num_missions; i++) {
1255 if (Campaign_mission_names[i] != NULL) {
1256 free(Campaign_mission_names[i]);
1257 Campaign_mission_names[i] = NULL;
1262 for (i=0; i<Num_campaigns; i++) {
1263 if (Campaign_file_names[i] != NULL) {
1264 free(Campaign_file_names[i]);
1265 Campaign_file_names[i] = NULL;
1270 for (i=0; i<Num_standalone_missions; i++) {
1271 if (Mission_filenames[i] != NULL) {
1272 free(Mission_filenames[i]);
1273 Mission_filenames[i] = NULL;
1277 // unload the overlay bitmap
1278 help_overlay_unload(SIM_ROOM_OVERLAY);
1280 campaign_mission_hash_table_delete();
1282 Ui_window.destroy();
1283 common_free_interface_palette(); // restore game palette
1286 // unload special mission icons
1287 sim_room_unload_mission_icons();
1290 // ---------------------------------------------------------------------
1291 // sim_room_do_frame()
1293 // Called once per frame to process user input for the sim_room Assignment Screen
1295 void sim_room_do_frame(float frametime)
1299 int font_height = gr_get_font_height();
1300 int select_tease_line = -1; // line mouse is down on, but won't be selected until button released
1302 if ( help_overlay_active(SIM_ROOM_OVERLAY) ) {
1303 Buttons[gr_screen.res][HELP_BUTTON].button.reset_status();
1304 Ui_window.set_ignore_gadgets(1);
1307 k = Ui_window.process() & ~KEY_DEBUGGED;
1309 if ( (k > 0) || B1_JUST_RELEASED ) {
1310 if ( help_overlay_active(SIM_ROOM_OVERLAY) ) {
1311 help_overlay_set_state(SIM_ROOM_OVERLAY, 0);
1312 Ui_window.set_ignore_gadgets(0);
1317 if ( !help_overlay_active(SIM_ROOM_OVERLAY) ) {
1318 Ui_window.set_ignore_gadgets(0);
1322 case SDLK_DOWN: // scroll list down
1323 sim_room_scroll_line_down();
1326 case SDLK_UP: // scroll list up
1327 sim_room_scroll_line_up();
1331 gameseq_post_event(GS_EVENT_MAIN_MENU);
1334 case KEY_CTRLED | SDLK_UP:
1335 sim_room_button_pressed(TECH_DATABASE_BUTTON);
1338 case KEY_CTRLED | SDLK_DOWN:
1339 sim_room_button_pressed(CUTSCENES_BUTTON);
1343 if (Player->readyroom_listing_mode == MODE_CAMPAIGNS)
1344 Player->readyroom_listing_mode = MODE_MISSIONS;
1346 Player->readyroom_listing_mode = MODE_CAMPAIGNS;
1348 Selected_line = Scroll_offset = 0;
1349 gamesnd_play_iface(SND_USER_SELECT);
1350 sim_room_build_listing();
1354 gamesnd_play_iface(SND_SWITCH_SCREENS);
1355 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
1359 for (i=0; i<NUM_BUTTONS; i++){
1360 if (Buttons[gr_screen.res][i].button.pressed()){
1361 if (sim_room_button_pressed(i)){
1367 for (i=0; i<LIST_BUTTONS_MAX; i++) {
1368 if (List_buttons[i].is_mouse_on())
1369 select_tease_line = i + Scroll_offset;
1371 if (List_buttons[i].pressed()) {
1372 Selected_line = i + Scroll_offset;
1373 gamesnd_play_iface(SND_USER_SELECT);
1377 GR_MAYBE_CLEAR_RES(Background_bitmap);
1378 if (Background_bitmap >= 0) {
1379 gr_set_bitmap(Background_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1385 for (i=TECH_DATABASE_BUTTON; i<=CREDITS_BUTTON; i++){
1386 if (Buttons[gr_screen.res][i].button.button_down()){
1391 if (i > CREDITS_BUTTON){
1392 Buttons[gr_screen.res][SIMULATOR_BUTTON].button.draw_forced(2);
1395 if (!Buttons[gr_screen.res][CAMPAIGN_TAB].button.button_down() && !Buttons[gr_screen.res][MISSION_TAB].button.button_down()) {
1396 if (Player->readyroom_listing_mode == MODE_CAMPAIGNS){
1397 Buttons[gr_screen.res][CAMPAIGN_TAB].button.draw_forced(2);
1398 } else if (Player->readyroom_listing_mode == MODE_MISSIONS){
1399 Buttons[gr_screen.res][MISSION_TAB].button.draw_forced(2);
1404 if (Player->readyroom_listing_mode == MODE_CAMPAIGNS) {
1405 gr_set_color_fast(&Color_text_heading);
1406 SDL_strlcpy(buf, Campaign.name, SDL_arraysize(buf));
1407 gr_force_fit_string(buf, 255, list_w1);
1408 gr_printf(list_x1, Mission_list_coords[gr_screen.res][1], buf);
1410 if (Campaign.filename[0]) {
1411 SDL_snprintf(buf, SDL_arraysize(buf), NOX("%s%s"), Campaign.filename, FS_CAMPAIGN_FILE_EXT);
1412 gr_force_fit_string(buf, 255, list_w2);
1413 gr_printf(list_x2, Mission_list_coords[gr_screen.res][1], buf);
1415 // blit the proper icons if necessary
1416 char full_name[256];
1417 SDL_strlcpy(full_name, cf_add_ext(Campaign.filename,FS_CAMPAIGN_FILE_EXT), SDL_arraysize(full_name));
1418 fs_builtin_mission *fb = game_find_builtin_mission(full_name);
1420 // sim_room_blit_icons(0, Mission_list_coords[gr_screen.res][1], fb, 0);
1425 line = Scroll_offset;
1426 while (sim_room_line_query_visible(line)) {
1427 y = list_y + sim_room_lines[line].y - sim_room_lines[Scroll_offset].y;
1429 if (sim_room_lines[line].type != READYROOM_LINE_CAMPAIGN) {
1430 List_buttons[line - Scroll_offset].update_dimensions(list_x1, y, list_x2 + list_w2 - list_x1, font_height);
1431 List_buttons[line - Scroll_offset].enable();
1434 List_buttons[line - Scroll_offset].disable();
1436 if (line == Selected_line)
1437 gr_set_color_fast(&Color_text_selected);
1438 else if (line == select_tease_line)
1439 gr_set_color_fast(&Color_text_subselected);
1441 gr_set_color_fast(&Color_text_normal);
1443 SDL_strlcpy(buf, sim_room_lines[line].name, SDL_arraysize(buf));
1444 gr_force_fit_string(buf, 255, list_x1 + list_w1 - sim_room_lines[line].x);
1445 gr_printf(sim_room_lines[line].x, y, buf);
1447 if (sim_room_lines[line].filename) {
1448 SDL_strlcpy(buf, sim_room_lines[line].filename, SDL_arraysize(buf));
1449 gr_force_fit_string(buf, 255, list_w2);
1450 gr_printf(list_x2, y, buf);
1453 // blit additional icon information
1454 sim_room_blit_icons(line, y);
1459 i = line - Scroll_offset;
1460 while (i < LIST_BUTTONS_MAX)
1461 List_buttons[i++].disable();
1463 // blit help overlay if active
1464 help_overlay_maybe_blit(SIM_ROOM_OVERLAY);
1469 void sim_room_blit_icons(int line_index, int y_start, fs_builtin_mission *fb, int is_md)
1471 int is_from_volition = 0;
1473 // determine icon status
1475 is_from_volition = (sim_room_lines[line_index].flags & READYROOM_FLAG_FROM_VOLITION) ? 1 : 0;
1477 is_md = (sim_room_lines[line_index].flags & READYROOM_FLAG_FROM_MDISK) ? 1 : 0;
1480 is_from_volition = (fb->flags & FSB_FROM_VOLITION) ? 1 : 0;
1482 is_md = (fb->flags & FSB_FROM_MDISK) ? 1 : 0;
1486 // if the line is flagged as a volition file
1487 if(is_from_volition && (Mission_icon_bitmaps[MISSION_ICON_VOLITION] >= 0)){
1488 gr_set_bitmap(Mission_icon_bitmaps[MISSION_ICON_VOLITION], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1489 gr_bitmap(Sim_volition_icon_x[gr_screen.res], y_start + MISSION_ICON_VOLITION_Y_OFFSET);
1493 if(is_md && (Mission_icon_bitmaps[MISSION_ICON_MDISK] >= 0)){
1494 gr_set_bitmap(Mission_icon_bitmaps[MISSION_ICON_MDISK], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1495 gr_bitmap(Sim_silent_icon_x[gr_screen.res], y_start + MISSION_ICON_MDISK_Y_OFFSET);
1500 /// Campaign room stuff below
1501 int Cr_list_coords[GR_NUM_RESOLUTIONS][4] = {
1514 int Cr_info_coords[GR_NUM_RESOLUTIONS][4] = {
1527 #define CR_NUM_BUTTONS 6
1529 #define CR_SCROLL_UP_BUTTON 0
1530 #define CR_SCROLL_DOWN_BUTTON 1
1531 #define CR_SCROLL_INFO_UP_BUTTON 2
1532 #define CR_SCROLL_INFO_DOWN_BUTTON 3
1533 #define CR_RESET_BUTTON 4
1534 #define CR_COMMIT_BUTTON 5
1536 #define MAX_INFO_LINES 20
1538 #define MAX_INFO_LINE_LEN 256
1540 ui_button_info Cr_buttons[GR_NUM_RESOLUTIONS][CR_NUM_BUTTONS] = {
1543 ui_button_info("CAB_00", 0, 95, -1, -1, 0), // scroll up
1544 ui_button_info("CAB_01", 0, 142, -1, -1, 1), // scroll down
1545 ui_button_info("CAB_02", 17, 259, -1, -1, 2), // info scroll up
1546 ui_button_info("CAB_03", 17, 307, -1, -1, 3), // info scroll down
1547 ui_button_info("CAB_07", 545, 323, -1, -1, 7), // reset
1548 ui_button_info("CAB_04", 561, 411, -1, -1, 4), // select
1550 ui_button_info("CAB_00", 2, 42, -1, -1, 0),
1551 ui_button_info("CAB_01", 2, 89, -1, -1, 1),
1552 ui_button_info("CAB_02", 2, 279, -1, -1, 2),
1553 ui_button_info("CAB_03", 2, 325, -1, -1, 3),
1554 ui_button_info("CAB_04", 579, 353, -1, -1, 4),
1555 ui_button_info("CAB_05", 575, 434, -1, -1, 5),
1559 ui_button_info("2_CAB_00", 3, 68, -1, -1, 0),
1560 ui_button_info("2_CAB_01", 3, 142, -1, -1, 1),
1561 ui_button_info("2_CAB_02", 3, 446, -1, -1, 2),
1562 ui_button_info("2_CAB_03", 3, 520, -1, -1, 3),
1563 ui_button_info("2_CAB_04", 927, 565, -1, -1, 4),
1564 ui_button_info("2_CAB_05", 920, 694, -1, -1, 5),
1570 #define CR_NUM_TEXT 3
1572 UI_XSTR Cr_text[GR_NUM_RESOLUTIONS][CR_NUM_TEXT] = {
1574 { "Restart", 1403, 569, 326, UI_XSTR_COLOR_GREEN, -1, &Cr_buttons[0][CR_RESET_BUTTON].button },
1575 { "Campaign", 1404, 569, 337, UI_XSTR_COLOR_GREEN, -1, &Cr_buttons[0][CR_RESET_BUTTON].button },
1576 { "Select", 1409, 568, 413, UI_XSTR_COLOR_PINK, -1, &Cr_buttons[0][CR_COMMIT_BUTTON].button },
1579 { "Restart", 1403, 922, 523, UI_XSTR_COLOR_GREEN, -1, &Cr_buttons[1][CR_RESET_BUTTON].button },
1580 { "Campaign", 1404, 922, 538, UI_XSTR_COLOR_GREEN, -1, &Cr_buttons[1][CR_RESET_BUTTON].button },
1581 { "Select", 1409, 921, 665, UI_XSTR_COLOR_PINK, -1, &Cr_buttons[1][CR_COMMIT_BUTTON].button },
1586 static int Num_desc_lines;
1587 static int Desc_scroll_offset;
1588 static int Selected_campaign_index;
1589 static int Active_campaign_index;
1591 char *Info_text_ptrs[MAX_INFO_LINES];
1592 int Num_info_lines, Info_text_line_size[MAX_INFO_LINES];
1594 void campaign_room_build_listing()
1596 int c, i, y, type, max_players;
1597 int font_height = gr_get_font_height();
1598 char name[NAME_LENGTH];
1601 if (!Campaign_names_inited) {
1602 for (i=0; i<Num_campaigns; i++) {
1603 Campaign_names[i] = NULL;
1604 if ( mission_campaign_get_info(Campaign_file_names[i], name, &type, &max_players) ) {
1606 if((game_find_builtin_mission(name) == NULL) && !strstr(name, "peterdrake")){
1609 Campaign_names[i] = strdup(name);
1612 if (type == CAMPAIGN_TYPE_SINGLE){
1613 Campaign_names[i] = strdup(name);
1619 Campaign_names_inited = 1;
1622 for (c=0; c<Num_campaigns; c++) {
1623 if (Campaign_names[c]) {
1625 // determine some extra information
1627 fs_builtin_mission *fb = game_find_builtin_mission(Campaign_file_names[c]);
1629 if(fb->flags & FSB_FROM_VOLITION){
1630 flags |= READYROOM_FLAG_FROM_VOLITION;
1633 if(fb->flags & FSB_FROM_MDISK){
1634 flags |= READYROOM_FLAG_FROM_MDISK;
1639 sim_room_line_add(READYROOM_LINE_CAMPAIGN, Campaign_names[c], Campaign_file_names[c], Cr_list_coords[gr_screen.res][0], y, flags);
1640 y += font_height + 2;
1645 void set_new_campaign_line(int n)
1649 Selected_campaign_index = n;
1650 str = Campaign_descs[Selected_campaign_index];
1653 Num_info_lines = split_str(str, Cr_info_coords[gr_screen.res][2], Info_text_line_size, Info_text_ptrs, MAX_INFO_LINES);
1654 SDL_assert(Num_info_lines >= 0);
1657 Desc_scroll_offset = 0;
1660 void campaign_room_scroll_info_up()
1662 if (Desc_scroll_offset) {
1663 Desc_scroll_offset--;
1664 gamesnd_play_iface(SND_SCROLL);
1667 gamesnd_play_iface(SND_GENERAL_FAIL);
1670 void campaign_room_scroll_info_down()
1672 if ( (Num_info_lines - Desc_scroll_offset) * gr_get_font_height() > Cr_info_coords[gr_screen.res][3]) {
1673 Desc_scroll_offset++;
1674 gamesnd_play_iface(SND_SCROLL);
1677 gamesnd_play_iface(SND_GENERAL_FAIL);
1680 // returns: 0 = success, !0 = aborted or failed
1681 int campaign_room_reset_campaign(int n)
1686 z = popup_sync(PF_TITLE_BIG | PF_TITLE_RED, 2, POPUP_CANCEL, POPUP_OK, XSTR( "Warning\nThis will cause all progress in your\ncurrent campaign to be lost", 110));
1688 int len = strlen(Campaign_file_names[n]) + 5;
1689 filename = (char *) malloc(len);
1690 SDL_strlcpy(filename, Campaign_file_names[n], len);
1691 SDL_strlcat(filename, FS_CAMPAIGN_FILE_EXT, len);
1693 mission_campaign_savefile_delete(filename);
1694 mission_campaign_load(filename);
1695 mission_campaign_next_mission();
1702 void campaign_room_commit()
1704 if (Selected_campaign_index < 0) {
1705 gamesnd_play_iface(SND_GENERAL_FAIL);
1709 if (SDL_strcasecmp(Campaign_file_names[Selected_campaign_index], Campaign.filename)) { // new campaign selected
1710 if ((Active_campaign_index >= 0) && campaign_room_reset_campaign(Active_campaign_index)) {
1711 gamesnd_play_iface(SND_GENERAL_FAIL);
1715 mission_campaign_savefile_delete(Campaign_file_names[Selected_campaign_index]);
1716 mission_campaign_load(Campaign_file_names[Selected_campaign_index]);
1717 SDL_strlcpy(Player->current_campaign, Campaign.filename, SDL_arraysize(Player->current_campaign)); // track new campaign for player
1720 if (mission_campaign_next_mission()) { // is campaign and next mission valid?
1721 gamesnd_play_iface(SND_GENERAL_FAIL);
1725 gameseq_post_event(GS_EVENT_MAIN_MENU);
1726 gamesnd_play_iface(SND_COMMIT_PRESSED);
1729 int campaign_room_button_pressed(int n)
1732 case CR_SCROLL_UP_BUTTON:
1733 sim_room_scroll_screen_up();
1736 case CR_SCROLL_DOWN_BUTTON:
1737 sim_room_scroll_screen_down();
1740 case CR_SCROLL_INFO_UP_BUTTON:
1741 campaign_room_scroll_info_up();
1744 case CR_SCROLL_INFO_DOWN_BUTTON:
1745 campaign_room_scroll_info_down();
1748 case CR_COMMIT_BUTTON:
1749 campaign_room_commit();
1753 case CR_HELP_BUTTON:
1754 launch_context_help();
1755 gamesnd_play_iface(SND_HELP_PRESSED);
1758 case CR_OPTIONS_BUTTON:
1759 gamesnd_play_iface(SND_SWITCH_SCREENS);
1760 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
1764 case CR_RESET_BUTTON:
1765 if ( (Active_campaign_index < 0) || (Active_campaign_index >= Num_campaigns) )
1766 gamesnd_play_iface(SND_GENERAL_FAIL);
1767 else if (campaign_room_reset_campaign(Active_campaign_index))
1768 gamesnd_play_iface(SND_GENERAL_FAIL);
1770 gamesnd_play_iface(SND_USER_SELECT);
1778 void campaign_room_init()
1780 int i, j, load_failed;
1784 list_h = Mission_list_coords[gr_screen.res][3];
1787 common_set_interface_palette("InterfacePalette"); // set the interface palette
1789 Ui_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0);
1790 Ui_window.set_mask_bmap(Campaign_mask_filename[gr_screen.res]);
1792 for (i=0; i<CR_NUM_BUTTONS; i++) {
1793 b = &Cr_buttons[gr_screen.res][i];
1795 b->button.create(&Ui_window, "", b->x, b->y, 60, 30, (i < 2), 1);
1796 // set up callback for when a mouse first goes over a button
1797 b->button.set_highlight_action(common_play_highlight_sound);
1798 b->button.set_bmaps(b->filename);
1799 b->button.link_hotspot(b->hotspot);
1802 for (i=0; i<LIST_BUTTONS_MAX; i++) {
1803 List_buttons[i].create(&Ui_window, "", 0, 0, 60, 30, 0, 1);
1804 List_buttons[i].hide();
1805 List_buttons[i].disable();
1810 for(i=0; i<CR_NUM_TEXT; i++){
1811 Ui_window.add_XSTR(&Cr_text[gr_screen.res][i]);
1815 // set up sim_rooms for buttons so we draw the correct animation frame when a key is pressed
1816 Cr_buttons[gr_screen.res][CR_SCROLL_UP_BUTTON].button.set_hotkey(SDLK_PAGEUP);
1817 Cr_buttons[gr_screen.res][CR_SCROLL_DOWN_BUTTON].button.set_hotkey(SDLK_PAGEDOWN);
1818 Cr_buttons[gr_screen.res][CR_RESET_BUTTON].button.set_hotkey(SDLK_DELETE);
1819 Cr_buttons[gr_screen.res][CR_COMMIT_BUTTON].button.set_hotkey(KEY_CTRLED | SDLK_RETURN);
1820 // Cr_buttons[gr_screen.res][CR_HELP_BUTTON].button.set_hotkey(KEY_F2);
1822 Background_bitmap = bm_load(Campaign_filename[gr_screen.res]);
1824 // load in help overlay bitmap
1825 help_overlay_load(CAMPAIGN_ROOM_OVERLAY);
1826 help_overlay_set_state(CAMPAIGN_ROOM_OVERLAY,0);
1830 Desc_scroll_offset = Scroll_offset = 0;
1831 load_failed = mission_load_up_campaign();
1833 mission_campaign_next_mission();
1836 Campaign.filename[0] = 0;
1837 Campaign.num_missions = 0;
1842 Get_file_list_filter = campaign_room_campaign_filter;
1843 SDL_snprintf(wild_card, SDL_arraysize(wild_card), "*%s", FS_CAMPAIGN_FILE_EXT);
1844 Num_campaigns = cf_get_file_list(MAX_CAMPAIGNS, Campaign_file_names, CF_TYPE_MISSIONS, wild_card, CF_SORT_NAME);
1846 for (i=0; i<Num_campaigns; i++) {
1847 for (j=0; j<Num_campaigns; j++) {
1848 if (Campaign_file_names_temp[j]) {
1849 if (!strncmp(Campaign_file_names[i], Campaign_file_names_temp[j], strlen(Campaign_file_names_temp[j]) - 4)) {
1850 Campaign_descs[i] = Campaign_descs_temp[j];
1851 free(Campaign_file_names_temp[j]);
1852 Campaign_file_names_temp[j] = NULL;
1858 SDL_assert(j < Num_campaigns); // Campaign not found? How is that possible?
1861 Campaign_names_inited = 0;
1862 campaign_room_build_listing();
1864 Selected_campaign_index = Active_campaign_index = -1;
1866 for (i=0; i<Num_campaigns; i++)
1867 if (!SDL_strcasecmp(Campaign_file_names[i], Campaign.filename)) {
1868 set_new_campaign_line(i);
1869 Active_campaign_index = i;
1875 void campaign_room_close()
1879 for (i=0; i<Num_campaigns; i++) {
1880 if (Campaign_descs[i] != NULL) {
1881 free(Campaign_descs[i]);
1882 Campaign_descs[i] = NULL;
1886 if (Background_bitmap >= 0)
1887 bm_unload(Background_bitmap);
1889 if (Campaign_names_inited) {
1890 for (i=0; i<Num_campaigns; i++) {
1891 if (Campaign_names[i] != NULL) {
1892 free(Campaign_names[i]);
1893 Campaign_names[i] = NULL;
1898 for (i=0; i<Num_campaigns; i++) {
1899 if (Campaign_file_names[i] != NULL) {
1900 free(Campaign_file_names[i]);
1901 Campaign_file_names[i] = NULL;
1905 // unload the overlay bitmap
1906 help_overlay_unload(CAMPAIGN_ROOM_OVERLAY);
1908 Ui_window.destroy();
1909 common_free_interface_palette(); // restore game palette
1913 void campaign_room_do_frame(float frametime)
1916 char line_text[MAX_INFO_LINE_LEN];
1918 int font_height = gr_get_font_height();
1919 int select_tease_line = -1; // line mouse is down on, but won't be selected until button released
1921 if ( help_overlay_active(CAMPAIGN_ROOM_OVERLAY) ) {
1922 // Cr_buttons[gr_screen.res][CR_HELP_BUTTON].button.reset_status();
1923 Ui_window.set_ignore_gadgets(1);
1926 k = Ui_window.process() & ~KEY_DEBUGGED;
1928 if ( (k > 0) || B1_JUST_RELEASED ) {
1929 if ( help_overlay_active(CAMPAIGN_ROOM_OVERLAY) ) {
1930 help_overlay_set_state(CAMPAIGN_ROOM_OVERLAY, 0);
1931 Ui_window.set_ignore_gadgets(0);
1936 if ( !help_overlay_active(CAMPAIGN_ROOM_OVERLAY) ) {
1937 Ui_window.set_ignore_gadgets(0);
1941 case SDLK_DOWN: // scroll list down
1942 if (Selected_campaign_index < Num_campaigns - 1) {
1943 set_new_campaign_line(Selected_campaign_index + 1);
1944 gamesnd_play_iface(SND_SCROLL);
1947 gamesnd_play_iface(SND_GENERAL_FAIL);
1951 case SDLK_UP: // scroll list up
1952 if (Selected_campaign_index < 0)
1953 Selected_campaign_index = 1;
1955 if (Selected_campaign_index) {
1956 set_new_campaign_line(Selected_campaign_index - 1);
1957 gamesnd_play_iface(SND_SCROLL);
1960 gamesnd_play_iface(SND_GENERAL_FAIL);
1965 gameseq_post_event(GS_EVENT_MAIN_MENU);
1969 for (i=0; i<CR_NUM_BUTTONS; i++){
1970 if (Cr_buttons[gr_screen.res][i].button.pressed()){
1971 if (campaign_room_button_pressed(i)){
1977 for (i=0; i<LIST_BUTTONS_MAX; i++) {
1978 if (List_buttons[i].is_mouse_on()){
1979 select_tease_line = i + Scroll_offset;
1982 if (List_buttons[i].pressed()) {
1983 set_new_campaign_line(i + Scroll_offset);
1984 gamesnd_play_iface(SND_USER_SELECT);
1988 GR_MAYBE_CLEAR_RES(Background_bitmap);
1989 if (Background_bitmap >= 0) {
1990 gr_set_bitmap(Background_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1997 line = Scroll_offset;
1998 while (sim_room_line_query_visible(line)) {
1999 y = Cr_list_coords[gr_screen.res][1] + sim_room_lines[line].y - sim_room_lines[Scroll_offset].y;
2001 List_buttons[line - Scroll_offset].update_dimensions(Cr_list_coords[gr_screen.res][0], y, Cr_list_coords[gr_screen.res][2], font_height);
2002 List_buttons[line - Scroll_offset].enable();
2004 if (!SDL_strcasecmp(sim_room_lines[line].filename, Campaign.filename)) {
2005 gr_set_color_fast(&Color_white);
2006 i = y + font_height / 2 - 1;
2007 gr_circle(Cr_list_coords[gr_screen.res][0] - 6, i, 5);
2009 gr_set_color_fast(&Color_bright_white);
2010 gr_line(Cr_list_coords[gr_screen.res][0] - 10, i, Cr_list_coords[gr_screen.res][0] - 8, i);
2011 gr_line(Cr_list_coords[gr_screen.res][0] - 6, i - 4, Cr_list_coords[gr_screen.res][0] - 6, i - 2);
2012 gr_line(Cr_list_coords[gr_screen.res][0] - 4, i, Cr_list_coords[gr_screen.res][0] - 2, i);
2013 gr_line(Cr_list_coords[gr_screen.res][0] - 6, i + 2, Cr_list_coords[gr_screen.res][0] - 6, i + 4);
2016 if (line == Selected_campaign_index)
2017 gr_set_color_fast(&Color_text_selected);
2018 else if (line == select_tease_line)
2019 gr_set_color_fast(&Color_text_subselected);
2021 gr_set_color_fast(&Color_text_normal);
2023 SDL_strlcpy(buf, sim_room_lines[line].name, SDL_arraysize(buf));
2024 gr_force_fit_string(buf, 255, Cr_list_coords[gr_screen.res][0] + Cr_list_coords[gr_screen.res][2] - sim_room_lines[line].x);
2025 gr_printf(sim_room_lines[line].x, y, buf);
2029 i = line - Scroll_offset;
2030 while (i < LIST_BUTTONS_MAX)
2031 List_buttons[i++].disable();
2034 i = Desc_scroll_offset;
2035 gr_set_color_fast(&Color_text_normal);
2037 while (y + font_height <= Cr_info_coords[gr_screen.res][3]) {
2038 if (i >= Num_info_lines)
2041 SDL_assert(Info_text_line_size[i] < MAX_INFO_LINE_LEN);
2042 int len = SDL_min(Info_text_line_size[i] + 1, MAX_INFO_LINE_LEN);
2043 SDL_strlcpy(line_text, Info_text_ptrs[i], len);
2044 drop_white_space(line_text);
2045 gr_string(Cr_info_coords[gr_screen.res][0], Cr_info_coords[gr_screen.res][1] + y, line_text);
2050 // blit help overlay if active
2051 help_overlay_maybe_blit(CAMPAIGN_ROOM_OVERLAY);
2056 void sim_room_load_mission_icons()
2061 for(idx=0; idx<NUM_MISSION_ICONS; idx++){
2062 Mission_icon_bitmaps[idx] = -1;
2063 Mission_icon_bitmaps[idx] = bm_load(Mission_icon_bitmap_filenames[idx]);
2067 void sim_room_unload_mission_icons()
2071 // unload all bitmaps
2072 for(idx=0; idx<NUM_MISSION_ICONS; idx++){
2073 if(Mission_icon_bitmaps[idx] >= 0){
2074 bm_unload(Mission_icon_bitmaps[idx]);
2075 Mission_icon_bitmaps[idx] = -1;