]> icculus.org git repositories - taylor/freespace2.git/blob - src/menuui/readyroom.cpp
Initial revision
[taylor/freespace2.git] / src / menuui / readyroom.cpp
1 /*
2  * $Logfile: /Freespace2/code/MenuUI/ReadyRoom.cpp $
3  * $Revision$
4  * $Date$
5  * $Author$
6  *
7  * Ready Room code, which is the UI screen for selecting Campaign/mission to play next mainly.
8  *
9  * $Log$
10  * Revision 1.1  2002/05/03 03:28:09  root
11  * Initial revision
12  *
13  * 
14  * 20    9/30/99 5:59p Jefff
15  * fixed compile error in OEM ver
16  * 
17  * 19    9/08/99 12:28p Jefff
18  * 
19  * 18    9/06/99 6:38p Dave
20  * Improved CD detection code.
21  * 
22  * 17    9/02/99 2:33p Jefff
23  * column heading coordinate changes
24  * 
25  * 16    8/22/99 4:16p Jefff
26  * scroll button coord fixes
27  * 
28  * 15    7/20/99 1:49p Dave
29  * Peter Drake build. Fixed some release build warnings.
30  * 
31  * 14    7/19/99 2:13p Dave
32  * Added some new strings for Heiko.
33  * 
34  * 13    7/15/99 9:20a Andsager
35  * FS2_DEMO initial checkin
36  * 
37  * 12    7/09/99 5:54p Dave
38  * Seperated cruiser types into individual types. Added tons of new
39  * briefing icons. Campaign screen.
40  * 
41  * 11    2/01/99 5:55p Dave
42  * Removed the idea of explicit bitmaps for buttons. Fixed text
43  * highlighting for disabled gadgets.
44  * 
45  * 10    1/30/99 9:01p Dave
46  * Coord fixes.
47  * 
48  * 9     1/30/99 5:08p Dave
49  * More new hi-res stuff.Support for nice D3D textures.
50  * 
51  * 8     1/29/99 12:47a Dave
52  * Put in sounds for beam weapon. A bunch of interface screens (tech
53  * database stuff).
54  * 
55  * 7     12/11/98 4:35p Andsager
56  * Fix sim room bug when no standalone missions
57  * 
58  * 6     12/07/98 5:02p Dan
59  * Removed improper use of bitmap filename with extension,
60  * 
61  * 5     10/13/98 9:28a Dave
62  * Started neatening up freespace.h. Many variables renamed and
63  * reorganized. Added AlphaColors.[h,cpp]
64  *    
65  *
66  * $NoKeywords: $
67  */
68
69 #include <ctype.h>
70
71 #include "2d.h"
72 #include "font.h"
73 #include "ui.h"
74 #include "uidefs.h"
75 #include "key.h"
76 #include "bmpman.h"
77 #include "gamesequence.h"
78 #include "missioncampaign.h"
79 #include "sound.h"
80 #include "gamesnd.h"
81 #include "missionscreencommon.h"
82 #include "freespace.h"
83 #include "missionparse.h"
84 #include "player.h"
85 #include "managepilot.h"
86 #include "popup.h"
87 #include "contexthelp.h"
88 #include "cfilesystem.h"
89 #include "freespace.h"
90 #include "alphacolors.h"
91
92 #define MAX_MISSIONS    1024
93
94 int Mission_list_coords[GR_NUM_RESOLUTIONS][4] = {
95         { // GR_640
96                 33, 108, 402, 279
97         },
98         { // GR_1024
99                 43, 175, 402, 279
100         }
101 };
102
103 int Campaign_list_coords[GR_NUM_RESOLUTIONS][4] = {
104         { // GR_640
105                 491, 108, 115, 279
106         },
107         { // GR_1024
108                 491, 175, 115, 279
109         }
110 };
111
112
113 // x coordinate offsets for data when campaign tab active
114 #define C_TEXT_X                0
115 #define C_SUBTEXT_X     19
116
117 // x coordinate offsets for data when mission tab active
118 #define M_TEXT_X                0
119
120 #define MODE_CAMPAIGNS  0
121 #define MODE_MISSIONS   1
122
123 #define MAX_LINES                                       200
124 #define MAX_DESC_LINES                  200
125 #define NUM_BUTTONS                             11
126 #define LIST_BUTTONS_MAX                40
127
128 #define SCROLL_UP_BUTTON                0
129 #define SCROLL_DOWN_BUTTON              1
130 #define MISSION_TAB                             2
131 #define CAMPAIGN_TAB                            3
132 #define HELP_BUTTON                             4
133 #define COMMIT_BUTTON                   5
134 #define OPTIONS_BUTTON                  6
135 #define TECH_DATABASE_BUTTON    7
136 #define SIMULATOR_BUTTON                8
137 #define CUTSCENES_BUTTON                9
138 #define CREDITS_BUTTON                  10
139
140 #define CAMPAIGN_MISSION_HASH_SIZE 307
141
142 struct sim_room_buttons {
143         char *filename;
144         int x, y, xt, yt;
145         int hotspot;
146         UI_BUTTON button;  // because we have a class inside this struct, we need the constructor below..
147
148         sim_room_buttons(char *name, int x1, int y1, int xt1, int yt1, int h) : filename(name), x(x1), y(y1), xt(xt1), yt(yt1), hotspot(h) {}
149 };
150
151 static sim_room_buttons Buttons[GR_NUM_RESOLUTIONS][NUM_BUTTONS] = {
152 //XSTR:OFF
153         {               // GR_640
154                 sim_room_buttons("LMB_04",              1,              99,     -1,     -1,     4),
155                 sim_room_buttons("LMB_05",              1,              381,    -1,     -1,     5),
156                 sim_room_buttons("LMB_06",              6,              438,    40,     445,    6),
157                 sim_room_buttons("LMB_07",              6,              457,    40,     462,    7),
158                 sim_room_buttons("LMB_08",              534,    426,    500,    440,    8),
159                 sim_room_buttons("LMB_09",              571,    426,    572,    413,    9),
160                 sim_room_buttons("LMB_10",              534,    455,    480,    462,    10),
161
162                 sim_room_buttons("TDB_00",              7,              3,              37,     7,              0),
163                 sim_room_buttons("TDB_01",              7,              18,     37,     23,     1),
164                 sim_room_buttons("TDB_02",              7,              34,     37,     38,     2),
165                 sim_room_buttons("TDB_03",              7,              49,     37,     54,     3),
166         },
167         {               // GR_1024
168                 sim_room_buttons("2_LMB_04",    2,              159,    -1,     -1,     4),
169                 sim_room_buttons("2_LMB_05",    2,              609,    -1,     -1,     5),
170                 sim_room_buttons("2_LMB_06",    10,     701,    64,     712,    6),
171                 sim_room_buttons("2_LMB_07",    10,     732,    64,     739,    7),
172                 sim_room_buttons("2_LMB_08",    854,    681,    800, 704,       8),
173                 sim_room_buttons("2_LMB_09",    914,    681,    915, 660,       9),
174                 sim_room_buttons("2_LMB_10",    854,    728,    800, 728,       10),
175
176                 sim_room_buttons("2_TDB_00",    12,     5,              59,     12,     0),
177                 sim_room_buttons("2_TDB_01",    12,     31,     59,     37,     1),
178                 sim_room_buttons("2_TDB_02",    12,     56,     59,     62,     2),
179                 sim_room_buttons("2_TDB_03",    12,     81,     59,     88,     3),
180         }
181 //XSTR:ON
182 };
183
184 char *Sim_filename[GR_NUM_RESOLUTIONS] = {
185         "LoadMission",
186         "2_LoadMission"
187 };
188 char *Sim_mask_filename[GR_NUM_RESOLUTIONS] = {
189         "LoadMission-m",
190         "2_LoadMission-m"
191 };
192
193 char *Campaign_filename[GR_NUM_RESOLUTIONS] = {
194         "Campaign",
195         "2_Campaign"
196 };
197 char *Campaign_mask_filename[GR_NUM_RESOLUTIONS] = {
198         "Campaign-m",
199         "2_Campaign-m"
200 };
201
202 // misc text. ("Mission" and "Filename"
203 #define NUM_SIM_MISC_TEXT                               2
204 #define SIM_MISC_TEXT_MISSION                   0
205 #define SIM_MISC_TEXT_FILENAME          1
206 int Sim_misc_text_coords[GR_NUM_RESOLUTIONS][NUM_SIM_MISC_TEXT][2] = {
207         { // GR_640
208                 {33, 95},
209                 {491, 95}
210         }, 
211         { // GR_1024
212                 {43, 155},
213                 {491, 155}
214         }
215 };
216
217 // readyroom text line stuff
218 #define READYROOM_LINE_CAMPAIGN 1
219 #define READYROOM_LINE_CMISSION 2
220 #define READYROOM_LINE_MISSION  3
221
222 #define READYROOM_FLAG_FROM_VOLITION                    (1<<0)                  // volition made
223 static struct { 
224         int type;                                       // see READYROOM_LINE_* defines above
225         char *name;
226         char *filename;
227         int x;                                          // X coordinate of line
228         int y;                                          // Y coordinate of line
229         int flags;                                      // special flags, see READYROOM_FLAG_* defines above
230 } sim_room_lines[MAX_LINES];
231
232 static char Cur_campaign[MAX_FILENAME_LEN];
233 static char *Mission_filenames[MAX_MISSIONS];
234 static char *Standalone_mission_names[MAX_MISSIONS];
235 static int  Standalone_mission_flags[MAX_MISSIONS];
236 static char *Campaign_missions[MAX_MISSIONS];
237 static char *Campaign_mission_names[MAX_CAMPAIGN_MISSIONS];
238 static int Campaign_mission_flags[MAX_MISSIONS];
239 static char *Campaign_descs[MAX_CAMPAIGNS];
240 static char *Campaign_descs_temp[MAX_CAMPAIGNS];
241 static char *Campaign_file_names_temp[MAX_CAMPAIGNS];
242 static int Standalone_mission_names_inited = 0;
243 static int Campaign_names_inited = 0;
244 static int Campaign_mission_names_inited = 0;
245 static int Num_standalone_missions;
246 static int Num_campaign_missions;
247 static int Num_player_missions;
248 static int Scroll_offset;
249 static int Selected_line;
250 static int Num_lines;
251 static int Num_campaign_missions_with_info = 0;
252 static int Num_standalone_missions_with_info = 0;
253 static int list_x1;
254 static int list_x2;
255 static int list_y;
256 static int list_w1;
257 static int list_w2;
258 static int list_h;
259 static int Background_bitmap;
260 static UI_WINDOW Ui_window;
261 static UI_BUTTON List_buttons[LIST_BUTTONS_MAX];  // buttons for each line of text in list
262
263 typedef struct hash_node {
264         hash_node *next;
265         char *filename;
266 } hash_node;
267
268 static hash_node *Campaign_mission_hash_table[CAMPAIGN_MISSION_HASH_SIZE];
269 static int Hash_table_inited;
270
271 // special icons (1.04 + stuff)
272 #define NUM_MISSION_ICONS                       1
273 #define MISSION_ICON_VOLITION           0                               // mini volition death's head :)
274
275 // icon offsets (see LIST_ defines above
276 //#define MISSION_ICON_VOLITION_X                               (46)
277 #define MISSION_ICON_VOLITION_Y_OFFSET          (-1)
278
279 // icon offsets
280 static int Sim_volition_icon_x[GR_NUM_RESOLUTIONS] = {
281         38,
282         49
283 };
284
285 // special icons themselves
286 int Mission_icon_bitmaps[NUM_MISSION_ICONS];
287 //XSTR:OFF
288 char *Mission_icon_bitmap_filenames[NUM_MISSION_ICONS] = {
289         "icon-volition" 
290 };
291 //XSTR:ON
292 void sim_room_load_mission_icons();
293 void sim_room_unload_mission_icons();
294 void sim_room_blit_icons(int line_index, int y_start, fs_builtin_mission *fb = NULL, int is_md = 0);
295
296 // Finds a hash value for mission filename
297 //
298 // returns hash value
299 int hash_filename(char *filename) {
300         unsigned __int64 hash_val = 0;
301         char *ptr = filename;
302         
303         // Dont hash .fsm extension, convert all to upper case
304         for (int i=0; i < ((signed int)(strlen(filename)) - 4); i++) {
305                 hash_val = (hash_val << 4) + toupper(*ptr++);
306         }
307
308         return int(hash_val % CAMPAIGN_MISSION_HASH_SIZE);
309 }
310
311 // insert filename into Campaign_mission_hash_table
312 //
313 // returns 1 if successful, 0 if could not allocate memory
314 int hash_insert(char *filename) {
315         int hash_val = hash_filename(filename);
316         hash_node *cur_node;
317
318         // Check if table empty
319         if (Campaign_mission_hash_table[hash_val] == NULL) {
320                 Campaign_mission_hash_table[hash_val] = new hash_node;
321
322                 cur_node = Campaign_mission_hash_table[hash_val];
323
324                 if (cur_node == NULL) {
325                         // Unable to allocate memory
326                         return 0;
327                 }
328         } else {
329                 // Walk down list to first empty node
330                 cur_node = Campaign_mission_hash_table[hash_val];
331                 while (cur_node->next != NULL) {
332                         cur_node = cur_node->next;
333                 }
334
335                 // Create new node
336                 cur_node->next = new hash_node;
337
338                 if (cur_node->next == NULL) {
339                         // unable to allocate memory
340                         return 0;
341                 } else {
342                         cur_node = cur_node->next;
343                 }
344         }
345
346         // Initialize data
347         cur_node->next = NULL;
348         cur_node->filename = filename;
349
350         // Return successs
351         return 1;
352 }
353
354 // Checks if a filename already exitst in the hash table
355 //
356 // returns 1 if found (collision), 0 if no collision
357 int campaign_mission_hash_collision(char *filename)
358 {
359         int hash_val = hash_filename(filename);
360         hash_node *cur_node = Campaign_mission_hash_table[hash_val];
361
362         if (cur_node == NULL) {
363                 return 0;
364         }
365
366         do {
367                 if (!stricmp(filename, cur_node->filename)) {
368                         return 1;
369                 }
370
371                 cur_node = cur_node->next;
372         } while (cur_node != NULL);
373
374         // Ran out of stuff to check
375         return 0;
376 }
377
378 // builds hash table of campaign mission filenames
379 //
380 // returns 1 if successful, 0 if not successful
381 int build_campaign_mission_filename_hash_table()
382 {
383         int rval;
384         // Go through all campaign missions
385         for (int i=0; i<Num_campaign_missions; i++) {
386                 rval = hash_insert(Campaign_missions[i]);
387                 if (rval == 0) {
388                         return 0;
389                 }
390         }
391
392         // successful
393         return 1;
394 }
395
396 // deletes hash table nodes
397 //
398 void campaign_mission_hash_table_delete()
399 {
400         hash_node *cur_node;
401
402         for (int i=0; i<CAMPAIGN_MISSION_HASH_SIZE; i++) {
403                 // Look for entries into array
404                 if (Campaign_mission_hash_table[i] != NULL) {
405                         cur_node = Campaign_mission_hash_table[i];
406
407                         // Walk down the list deleting self
408                         while (cur_node->next != NULL) {
409                                 hash_node *temp = cur_node->next;
410                                 delete cur_node;
411                                 cur_node = temp;
412                         }
413
414                         // Delete last node
415                         delete cur_node;
416                         Campaign_mission_hash_table[i] = NULL;
417                 }
418         }
419 }
420
421
422 // add a line of sim_room smuck to end of list
423 int sim_room_line_add(int type, char *name, char *filename, int x, int y, int flags)
424 {
425         if (Num_lines >= MAX_LINES)
426                 return 0;
427
428         sim_room_lines[Num_lines].type = type;
429         sim_room_lines[Num_lines].name = name;
430         sim_room_lines[Num_lines].filename = filename;
431         sim_room_lines[Num_lines].x = x;
432         sim_room_lines[Num_lines].y = y;
433         sim_room_lines[Num_lines].flags = flags;
434         return Num_lines++;
435 }
436
437 // filter out all multiplayer campaigns
438 int campaign_room_campaign_filter(char *filename)
439 {
440         int type, max_players;
441         char name[NAME_LENGTH], *desc = NULL;
442
443         #ifdef OEM_BUILD
444         // also need to check if this is the builtin campaign
445         if ( game_find_builtin_mission(filename) && mission_campaign_get_info(filename, name, &type, &max_players, &desc) ) {
446         #else
447         if ( mission_campaign_get_info(filename, name, &type, &max_players, &desc) ) {
448         #endif
449                 if ( type == CAMPAIGN_TYPE_SINGLE ) {                   
450                         Campaign_file_names_temp[Num_campaigns] = strdup(filename);
451                         Campaign_descs_temp[Num_campaigns++] = desc;
452                         return 1;                       
453                 }
454         }
455
456         if (desc){
457                 free(desc);
458         }
459
460         return 0;
461 }
462
463 // build up a list of all missions in all campaigns.
464 int sim_room_campaign_mission_filter(char *filename)
465 {
466         int num;
467
468         num = mission_campaign_get_mission_list(filename, &Campaign_missions[Num_campaign_missions], MAX_CAMPAIGN_MISSIONS - Num_campaign_missions);
469         if (num < 0)
470                 return 0;
471
472         Num_campaign_missions += num;
473         return 1;
474 }
475
476 // filter out all missions already used in existing campaigns
477 int sim_room_standalone_mission_filter(char *filename)
478 {
479         int type;
480         char mission_name[255];
481
482         // Check if a campaign mission (single and multi)
483         if (campaign_mission_hash_collision(filename)) {
484                 return 0;
485         } 
486
487         // Check if a standalone multi mission OR Mdisk mission with data
488         type = mission_parse_is_multi(filename, mission_name);
489         if (type && !(type & MISSION_TYPE_SINGLE))
490                 return 0;
491
492         return 1;
493 }
494
495 // builds up list of standalone missions and adds them to missions simulator
496 // processes one mission per frame
497 //
498 // returns 1 if finished with all missions, 0 otherwise
499 //
500 int build_standalone_mission_list_do_frame()
501 {
502         int font_height = gr_get_font_height();
503         char filename[MAX_FILENAME_LEN];
504         char str[256];
505         
506         // When no standalone missions in data directory
507         if (Num_standalone_missions == 0) {
508                 Standalone_mission_names_inited = 1;
509                 return 1;
510         }
511
512         // Set global variable so we we'll have list available next time
513         Standalone_mission_names[Num_standalone_missions_with_info] = NULL;
514         Standalone_mission_flags[Num_standalone_missions_with_info] = 0;
515
516         if (Num_standalone_missions > 0) {  // sanity check
517                 if (strlen(Mission_filenames[Num_standalone_missions_with_info]) < MAX_FILENAME_LEN - 4) { // sanity check?
518                         strcpy(filename, Mission_filenames[Num_standalone_missions_with_info]);
519
520                         // update popup         
521                         memset(str, 0, 256);
522                         sprintf(str, XSTR("Single Mission\n\n%s",989), filename);
523                         popup_change_text(str);
524
525                         // tack on an extension
526                         strcat(filename, FS_MISSION_FILE_EXT);
527                         if (!get_mission_info(filename)) {                      
528                                 Standalone_mission_names[Num_standalone_missions_with_info] = strdup(The_mission.name);
529                                 Standalone_mission_flags[Num_standalone_missions_with_info] = The_mission.game_type;
530                                 int y = Num_lines * (font_height + 2);
531
532                                 // determine some extra information
533                                 int flags = 0;
534                                 fs_builtin_mission *fb = game_find_builtin_mission(filename);                           
535                                 if((fb != NULL) && (fb->flags & FSB_FROM_VOLITION)){
536                                         flags |= READYROOM_FLAG_FROM_VOLITION;
537                                 }                               
538
539                                 // add the line
540                                 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);                     
541                         }
542                 }
543
544                 Num_standalone_missions_with_info++;
545         }
546
547         if (Num_standalone_missions_with_info == Num_standalone_missions) {
548                 Standalone_mission_names_inited = 1;
549                 return 1;
550         } else {
551                 return 0;
552         }
553 }
554
555 // builds up list of already played missions in a campaign and adds them to missions simulator
556 // processes one mission per frame
557 //
558 // returns 1 if finished with all missions, 0 otherwise
559 //
560 int build_campaign_mission_list_do_frame()
561 {
562         int font_height = gr_get_font_height();
563         char str[256];
564
565         // When no campaign files in data directory
566         if (Campaign.num_missions == 0) {
567                 Campaign_mission_names_inited = 1;
568                 return 1;
569         }
570
571         // change popup
572         memset(str, 0, 256);
573         sprintf(str, XSTR("Campaign Mission\n\n%s",990), Campaign.missions[Num_campaign_missions_with_info].name);
574         popup_change_text(str);
575
576         // Set global variable so we we'll have list available next time
577         Campaign_mission_names[Num_campaign_missions_with_info] = NULL;
578         Campaign_mission_flags[Num_campaign_missions_with_info] = 0;
579         
580         // Only allow missions already completed
581         if (Campaign.missions[Num_campaign_missions_with_info].completed) {
582                 if (!get_mission_info(Campaign.missions[Num_campaign_missions_with_info].name)) {
583                         // add to list
584                         Campaign_mission_names[Num_campaign_missions_with_info] = strdup(The_mission.name);
585                         Campaign_mission_flags[Num_campaign_missions_with_info] = The_mission.game_type;
586                         int y = Num_campaign_missions_with_info * (font_height + 2);
587
588                         // determine some extra information
589                         int flags = 0;
590                         fs_builtin_mission *fb = game_find_builtin_mission(Campaign.missions[Num_campaign_missions_with_info].name);                            
591                         if((fb != NULL) && (fb->flags & FSB_FROM_VOLITION)){
592                                 flags |= READYROOM_FLAG_FROM_VOLITION;
593                         }                               
594         
595                         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);
596                 }
597         }
598
599         Num_campaign_missions_with_info++;
600
601         if (Num_campaign_missions_with_info == Campaign.num_missions) {
602                 Campaign_mission_names_inited = 1;
603                 return 1;
604         } else {
605                 return 0;
606         }
607 }
608
609 // Builds list of either (1) standalone missions or (2) already played missions in current campaign
610 // First time through, it builds list, next time it uses precompiled list
611 void sim_room_build_listing()
612 {
613         int i, y;
614         int font_height = gr_get_font_height();
615         char full_filename[256];
616
617         Num_lines = y = 0;
618         list_y = Mission_list_coords[gr_screen.res][1];
619         list_h = Mission_list_coords[gr_screen.res][3];
620
621         // Stand alone single player missions.
622         if (Player->readyroom_listing_mode == MODE_MISSIONS) {
623                 if (Hash_table_inited) {
624                         if (!Standalone_mission_names_inited) {  // Is this the first time through
625                                 // build_list_do_frame builds list and adds sim room line and sets Standalone_mission_names_inited
626                                 popup_till_condition(build_standalone_mission_list_do_frame, POPUP_CANCEL, XSTR("Loading missions", 991) );
627                         } else {
628                                 for (i=0; i<Num_standalone_missions_with_info; i++) {
629                                         if (Standalone_mission_names[i]) {
630                                                 // determine some extra information
631                                                 int flags = 0;
632                                                 memset(full_filename, 0, 256);
633                                                 strcpy(full_filename, cf_add_ext(Mission_filenames[i], FS_MISSION_FILE_EXT));
634                                                 fs_builtin_mission *fb = game_find_builtin_mission(full_filename);                                              
635                                                 if((fb != NULL) && (fb->flags & FSB_FROM_VOLITION)){
636                                                         flags |= READYROOM_FLAG_FROM_VOLITION;
637                                                 }
638                                                 
639                                                 sim_room_line_add(READYROOM_LINE_MISSION, Standalone_mission_names[i], Mission_filenames[i], list_x1 + M_TEXT_X, y, flags);
640                                                 y += font_height + 2;
641                                         }
642                                 }
643                         }
644                 }
645         } else {
646                 // Campaign missions
647                 list_y += font_height + 2;
648                 list_h -= font_height - 2;
649
650                 if (!Campaign_mission_names_inited) {  // Is this the first time through
651                         popup_till_condition(build_campaign_mission_list_do_frame, POPUP_CANCEL, XSTR("Loading campaign missions",992));
652                         // builds list, adds sim room line and sets Campaign_mission_names_inited
653                 } else {
654                         for (i=0; i<Num_campaign_missions_with_info; i++) {
655                                 if (Campaign_mission_names[i]) {
656                                         // determine some extra information
657                                         int flags = 0;
658                                         memset(full_filename, 0, 256);
659                                         strcpy(full_filename, cf_add_ext(Campaign.missions[i].name, FS_MISSION_FILE_EXT));
660                                         fs_builtin_mission *fb = game_find_builtin_mission(full_filename);
661                                         if((fb != NULL) && (fb->flags & FSB_FROM_VOLITION)){
662                                                 flags |= READYROOM_FLAG_FROM_VOLITION;
663                                         }                                       
664
665                                         sim_room_line_add(READYROOM_LINE_CMISSION, Campaign_mission_names[i], Campaign.missions[i].name, list_x1 + C_SUBTEXT_X, y, flags);
666                                         y += font_height;
667                                 }
668                         }
669                 }
670         }
671 }
672
673 int sim_room_line_query_visible(int n)
674 {
675         int y;
676
677         if ((n < 0) || (n >= Num_lines))
678                 return 0;
679         
680         y = sim_room_lines[n].y - sim_room_lines[Scroll_offset].y;
681         if ((y < 0) || (y + gr_get_font_height() > list_h))
682                 return 0;
683
684         return 1;
685 }
686
687 void sim_room_scroll_screen_up()
688 {
689         if (Player->readyroom_listing_mode != MODE_MISSIONS) {
690                 if (Scroll_offset) {
691                         Scroll_offset--;
692                         gamesnd_play_iface(SND_SCROLL);
693
694                 } else
695                         gamesnd_play_iface(SND_GENERAL_FAIL);
696
697                 return;
698         }
699
700         if (Scroll_offset) {
701                 Scroll_offset--;
702                 Assert(Selected_line > Scroll_offset);
703                 while (!sim_room_line_query_visible(Selected_line))
704                         Selected_line--;
705
706                 gamesnd_play_iface(SND_SCROLL);
707
708         } else
709                 gamesnd_play_iface(SND_GENERAL_FAIL);
710 }
711
712 void sim_room_scroll_line_up()
713 {
714         if (Selected_line) {
715                 Selected_line--;
716                 if (Selected_line < Scroll_offset)
717                         Scroll_offset = Selected_line;
718
719                 gamesnd_play_iface(SND_SCROLL);
720
721         } else
722                 gamesnd_play_iface(SND_GENERAL_FAIL);
723 }
724
725 void sim_room_scroll_screen_down()
726 {
727         if (Player->readyroom_listing_mode != MODE_MISSIONS) {
728                 if (sim_room_lines[Num_lines - 1].y + gr_get_font_height() > sim_room_lines[Scroll_offset].y + list_h) {
729                         Scroll_offset++;
730                         gamesnd_play_iface(SND_SCROLL);
731
732                 } else
733                         gamesnd_play_iface(SND_GENERAL_FAIL);
734
735                 return;
736         }
737
738         if (sim_room_lines[Num_lines - 1].y + gr_get_font_height() > sim_room_lines[Scroll_offset].y + list_h) {
739                 Scroll_offset++;
740                 while (!sim_room_line_query_visible(Selected_line)) {
741                         Selected_line++;
742                         Assert(Selected_line < Num_lines);
743                 }
744
745                 gamesnd_play_iface(SND_SCROLL);
746
747         } else
748                 gamesnd_play_iface(SND_GENERAL_FAIL);
749 }
750
751 void sim_room_scroll_line_down()
752 {
753         if (Selected_line < Num_lines - 1) {
754                 Selected_line++;
755
756                 Assert(Selected_line > Scroll_offset);
757                 while (!sim_room_line_query_visible(Selected_line))
758                         Scroll_offset++;
759
760                 gamesnd_play_iface(SND_SCROLL);
761
762         } else
763                 gamesnd_play_iface(SND_GENERAL_FAIL);
764 }
765
766 // returns: 0 = success, !0 = aborted or failed
767 int ready_room_reset_campaign()
768 {
769         int z, rval = 1;
770
771         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) );
772
773         if (z) {
774                 mission_campaign_savefile_delete(Campaign.filename);
775                 mission_campaign_load(Campaign.filename);
776                 rval = 0;
777         }
778
779         return rval;
780 }
781
782 // Decide if we should offer choice to resume this savegame
783 int sim_room_can_resume_savegame(char *savegame_filename)
784 {
785         #ifdef FREESPACE_SAVERESTORE_SYSTEM
786         char savegame_mission[MAX_FILENAME_LEN];
787
788         if (state_read_description(savegame_filename, NULL, savegame_mission)) {
789                 return 0;
790         }
791
792         if (stricmp(Game_current_mission_filename, savegame_mission)) {
793                 return 0;
794         }
795
796         return 1;
797         #else
798         return 0;
799         #endif
800 }
801
802 // Decide wether to resume a save game or not
803 // exit:        1       =>      savegame has been restored
804 //                      0       =>      no restore, proceed to briefing
805 //                      -1      =>      don't start mission at all
806 int sim_room_maybe_resume_savegame()
807 {
808         // MWA -- 3/26/98 -- removed all savegame references in game
809         return 0;
810
811         /*
812         char savegame_filename[_MAX_FNAME];
813         int popup_rval = -1, resume_savegame = 0;
814
815         // Generate the save-game filename for this campaign
816         memset(savegame_filename, 0, _MAX_FNAME);
817         mission_campaign_savefile_generate_root(savegame_filename);
818         strcat(savegame_filename, NOX("svg"));
819
820         // Decide if we should offer choice to resume this savegame
821         if ( sim_room_can_resume_savegame(savegame_filename) ) {
822                 popup_rval = popup(0, 3, XSTR("&Cancel",-1), XSTR("&Overwrite",-1), XSTR("&Resume",-1), XSTR("A save game for this mission exists.", -1));
823                 switch ( popup_rval ) {
824                 case 0:
825                 case -1:
826                         resume_savegame = -1;
827                         break;
828                 case 1:
829                         resume_savegame = 0;
830                         break;
831                 case 2:
832                         resume_savegame = 1;
833                         break;
834                 default:
835                         Int3();
836                         resume_savegame = -1;
837                         break;
838                 }
839         } else {
840                 resume_savegame = 0;
841         }
842
843         if (resume_savegame == 1) {
844                 if ( state_restore_all(savegame_filename) == -1 ) {
845                         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));
846                         if (popup_rval == 1) {
847                                 resume_savegame = 0;
848                         } else {
849                                 resume_savegame = -1;
850                         }
851
852                 } else {
853                         resume_savegame = 1;
854                 }
855         }
856
857         // If we are resuming this savegame, then delete the file
858         if (resume_savegame == 1) {
859                 cf_delete(savegame_filename);
860         }
861
862         return resume_savegame;
863         */
864 }
865
866 int readyroom_continue_campaign()
867 {
868         if (mission_campaign_next_mission()) {  // is campaign and next mission valid?
869
870 #ifdef FS2_DEMO
871                 int reset_campaign = 0;
872                 reset_campaign = popup(PF_BODY_BIG, 2, POPUP_NO, POPUP_YES, XSTR( "Demo Campaign Is Over.  Would you like to play the campaign again?", 111) );
873                 if ( reset_campaign == 1 ) {
874                         mission_campaign_savefile_delete(Campaign.filename);
875                         mission_campaign_load(Campaign.filename);
876                         mission_campaign_next_mission();
877                 } else {
878                         return -1;
879                 }
880 #else
881                 gamesnd_play_iface(SND_GENERAL_FAIL);
882                 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) );
883                 return -1;
884 #endif
885         }
886
887         // CD CHECK
888         if(!game_do_cd_mission_check(Game_current_mission_filename)){           
889                 return -1;
890         }
891
892         // set the bit for campaign mode
893         Game_mode |= GM_CAMPAIGN_MODE;
894         gameseq_post_event( GS_EVENT_START_GAME );      
895
896         return 0;
897 }
898
899 void sim_room_commit()
900 {
901         if ((Selected_line >= Num_lines) || !sim_room_lines[Selected_line].filename) {
902                 gamesnd_play_iface(SND_GENERAL_FAIL);
903                 return;
904         }
905
906         strncpy(Game_current_mission_filename, sim_room_lines[Selected_line].filename, MAX_FILENAME_LEN);
907
908         Game_mode &= ~(GM_CAMPAIGN_MODE);                                               // be sure this bit is clear
909
910         // CD CHECK
911         if(game_do_cd_mission_check(Game_current_mission_filename)){            
912                 // don't resume savegame, proceed to briefing
913                 gameseq_post_event(GS_EVENT_START_GAME);
914                 gamesnd_play_iface(SND_COMMIT_PRESSED);
915         }
916 }
917
918 int sim_room_button_pressed(int n)
919 {
920         switch (n) {
921                 case SCROLL_UP_BUTTON:
922                         sim_room_scroll_screen_up();
923                         break;
924
925                 case SCROLL_DOWN_BUTTON:
926                         sim_room_scroll_screen_down();
927                         break;
928
929                 case MISSION_TAB:
930 #ifdef OEM_BUILD
931                         game_feature_not_in_demo_popup();
932 //                      gamesnd_play_iface(SND_GENERAL_FAIL);
933                         break;
934 #else
935                         Player->readyroom_listing_mode = MODE_MISSIONS;
936                         Selected_line = Scroll_offset = 0;
937                         gamesnd_play_iface(SND_USER_SELECT);
938                         sim_room_build_listing();
939                         break;
940 #endif
941
942                 case CAMPAIGN_TAB:
943                         Player->readyroom_listing_mode = MODE_CAMPAIGNS;
944                         Scroll_offset = 0;
945                         gamesnd_play_iface(SND_USER_SELECT);
946                         sim_room_build_listing();
947                         break;
948
949                 case COMMIT_BUTTON:
950                         sim_room_commit();
951                         break;
952
953                 case HELP_BUTTON:
954                         launch_context_help();
955                         gamesnd_play_iface(SND_HELP_PRESSED);
956                         break;
957
958                 case OPTIONS_BUTTON:
959                         gamesnd_play_iface(SND_SWITCH_SCREENS);
960                         gameseq_post_event(GS_EVENT_OPTIONS_MENU);
961                         return 1;
962
963                 case TECH_DATABASE_BUTTON:
964                         gamesnd_play_iface(SND_SWITCH_SCREENS);
965                         gameseq_post_event(GS_EVENT_TECH_MENU);
966                         return 1;
967
968                 case CUTSCENES_BUTTON:
969                         gamesnd_play_iface(SND_SWITCH_SCREENS);
970                         gameseq_post_event(GS_EVENT_GOTO_VIEW_CUTSCENES_SCREEN);
971                         return 1;
972
973                 case CREDITS_BUTTON:
974                         gamesnd_play_iface(SND_SWITCH_SCREENS);
975                         gameseq_post_event(GS_EVENT_CREDITS);
976                         return 1;
977         }
978
979         return 0;
980 }
981
982 // ---------------------------------------------------------------------
983 // mission_sim_room_init()
984 //
985 // Initialize the sim_room assignment screen system.  Called when GS_STATE_sim_room_SCREEN
986 // is entered.
987 //
988 void sim_room_init()
989 {
990         int i;
991         sim_room_buttons *b;
992         char wild_card[256];
993
994         list_x1 = Mission_list_coords[gr_screen.res][0];
995         list_x2 = Campaign_list_coords[gr_screen.res][0];
996         list_y = Mission_list_coords[gr_screen.res][1];
997         list_w1 = Mission_list_coords[gr_screen.res][2];
998         list_w2 = Campaign_list_coords[gr_screen.res][2];
999         list_h = Mission_list_coords[gr_screen.res][3];
1000
1001         // force mode to valid range.  I once had a bogus value here.  Don't know how that happened, though.
1002         if (Player->readyroom_listing_mode != MODE_MISSIONS)
1003                 Player->readyroom_listing_mode = MODE_CAMPAIGNS;
1004
1005         *Game_current_mission_filename = 0;
1006         common_set_interface_palette("InterfacePalette");  // set the interface palette
1007         Ui_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0);
1008         Ui_window.set_mask_bmap(Sim_mask_filename[gr_screen.res]);
1009
1010         for (i=0; i<NUM_BUTTONS; i++) {
1011                 b = &Buttons[gr_screen.res][i];
1012
1013                 b->button.create(&Ui_window, "", b->x, b->y, 60, 30, (i < 2), 1);
1014                 // set up callback for when a mouse first goes over a button
1015                 b->button.set_highlight_action(common_play_highlight_sound);
1016                 b->button.set_bmaps(b->filename);
1017                 b->button.link_hotspot(b->hotspot);
1018         }
1019
1020         // screen/button specific text
1021         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);
1022         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);
1023         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);
1024         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);
1025         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);
1026         
1027         // common tab button text
1028         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);
1029         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);
1030         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);
1031         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);
1032
1033         // misc text - not associated with any buttons
1034         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);
1035         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);
1036
1037         for (i=0; i<LIST_BUTTONS_MAX; i++) {
1038                 List_buttons[i].create(&Ui_window, "", 0, 0, 60, 30, 0, 1);
1039                 List_buttons[i].hide();
1040                 List_buttons[i].disable();
1041         }
1042
1043         // set up sim_rooms for buttons so we draw the correct animation frame when a key is pressed
1044         Buttons[gr_screen.res][SCROLL_UP_BUTTON].button.set_hotkey(KEY_PAGEUP);
1045         Buttons[gr_screen.res][SCROLL_DOWN_BUTTON].button.set_hotkey(KEY_PAGEDOWN);
1046         Buttons[gr_screen.res][COMMIT_BUTTON].button.set_hotkey(KEY_CTRLED | KEY_ENTER);        
1047
1048         Background_bitmap = bm_load(Sim_filename[gr_screen.res]);
1049
1050         // load in help overlay bitmap  
1051         help_overlay_load(SIM_ROOM_OVERLAY);
1052         help_overlay_set_state(SIM_ROOM_OVERLAY,0);
1053
1054         Scroll_offset = Selected_line = 0;
1055
1056         strcpy(Cur_campaign, Player->current_campaign);
1057         mission_load_up_campaign();
1058         mission_campaign_next_mission();
1059
1060         Num_campaigns = Num_campaign_missions = 0;
1061         Get_file_list_filter = sim_room_campaign_mission_filter;
1062         memset(wild_card, 0, 256);
1063         strcpy(wild_card, NOX("*"));
1064         strcat(wild_card, FS_CAMPAIGN_FILE_EXT);
1065         Num_campaigns = cf_get_file_list(MAX_CAMPAIGNS, Campaign_file_names, CF_TYPE_MISSIONS, wild_card, CF_SORT_NAME);
1066
1067         Hash_table_inited = 0;
1068         if (build_campaign_mission_filename_hash_table()) {
1069                 Hash_table_inited = 1;
1070         }
1071
1072         // HACK
1073         GR_MAYBE_CLEAR_RES(Background_bitmap);
1074         if(Background_bitmap != -1){
1075                 gr_set_bitmap(Background_bitmap);
1076                 gr_bitmap(0, 0);
1077         }
1078         Ui_window.draw();
1079         gr_flip();              
1080
1081         Get_file_list_filter = sim_room_standalone_mission_filter;
1082         memset(wild_card, 0, 256);
1083         strcpy(wild_card, NOX("*"));
1084         strcat(wild_card, FS_MISSION_FILE_EXT);
1085         Num_standalone_missions = cf_get_file_list(MAX_MISSIONS, Mission_filenames, CF_TYPE_MISSIONS, wild_card, CF_SORT_NAME);
1086
1087         Num_campaign_missions_with_info = Num_standalone_missions_with_info = Standalone_mission_names_inited = Campaign_names_inited = Campaign_mission_names_inited = 0;
1088         sim_room_build_listing();
1089
1090         // load special mission icons
1091         sim_room_load_mission_icons();
1092 }
1093
1094 // ---------------------------------------------------------------------
1095 // sim_room_close()
1096 //
1097 // Cleanup the sim_room assignment screen system.  Called when GS_STATE_sim_room_SCREEN
1098 // is left.
1099 //
1100 void sim_room_close()
1101 {
1102         int i;
1103
1104         for (i=0; i<Num_campaign_missions; i++)
1105                 if (Campaign_missions[i])
1106                         free(Campaign_missions[i]);
1107
1108         if (Background_bitmap >= 0)
1109                 bm_unload(Background_bitmap);
1110
1111         if (Standalone_mission_names_inited){
1112                 for (i=0; i<Num_standalone_missions; i++){
1113                         if (Standalone_mission_names[i]){
1114                                 free(Standalone_mission_names[i]);
1115                         }
1116                         Standalone_mission_flags[i] = 0;
1117                 }
1118         }
1119
1120         if (Campaign_names_inited)
1121                 for (i=0; i<Num_campaigns; i++)
1122                         if (Campaign_names[i])
1123                                 free(Campaign_names[i]);
1124
1125         if (Campaign_mission_names_inited)
1126                 for (i=0; i<Campaign.num_missions; i++)
1127                         if (Campaign_mission_names[i])
1128                                 free(Campaign_mission_names[i]);
1129
1130         for (i=0; i<Num_campaigns; i++)
1131                 free(Campaign_file_names[i]);
1132
1133         for (i=0; i<Num_standalone_missions; i++)
1134                 free(Mission_filenames[i]);
1135
1136         // unload the overlay bitmap
1137         help_overlay_unload(SIM_ROOM_OVERLAY);
1138
1139         campaign_mission_hash_table_delete();
1140
1141         Ui_window.destroy();
1142         common_free_interface_palette();                // restore game palette
1143         write_pilot_file();
1144
1145         // unload special mission icons
1146         sim_room_unload_mission_icons();
1147 }
1148
1149 // ---------------------------------------------------------------------
1150 // sim_room_do_frame()
1151 //
1152 // Called once per frame to process user input for the sim_room Assignment Screen
1153 //
1154 void sim_room_do_frame(float frametime)
1155 {
1156         char buf[256];
1157         int i, k, y, z, line;
1158         int font_height = gr_get_font_height();
1159         int select_tease_line = -1;  // line mouse is down on, but won't be selected until button released      
1160
1161         z = -1;
1162         for (i=0; i<Num_campaigns; i++)
1163                 if (!stricmp(Campaign_file_names[i], Campaign.filename)) {
1164                         z = i;
1165                         break;
1166                 }
1167
1168         if ( help_overlay_active(SIM_ROOM_OVERLAY) ) {
1169                 Buttons[gr_screen.res][HELP_BUTTON].button.reset_status();
1170                 Ui_window.set_ignore_gadgets(1);
1171         }
1172
1173         k = Ui_window.process() & ~KEY_DEBUGGED;
1174
1175         if ( (k > 0) || B1_JUST_RELEASED ) {
1176                 if ( help_overlay_active(SIM_ROOM_OVERLAY) ) {
1177                         help_overlay_set_state(SIM_ROOM_OVERLAY, 0);
1178                         Ui_window.set_ignore_gadgets(0);
1179                         k = 0;
1180                 }
1181         }
1182
1183         if ( !help_overlay_active(SIM_ROOM_OVERLAY) ) {
1184                 Ui_window.set_ignore_gadgets(0);
1185         }
1186
1187         switch (k) {
1188                 case KEY_DOWN:  // scroll list down
1189                         sim_room_scroll_line_down();
1190                         break;
1191
1192                 case KEY_UP:  // scroll list up
1193                         sim_room_scroll_line_up();
1194                         break;
1195
1196                 case KEY_ESC:
1197                         gameseq_post_event(GS_EVENT_MAIN_MENU);
1198                         break;
1199
1200                 case KEY_CTRLED | KEY_UP:
1201                         sim_room_button_pressed(TECH_DATABASE_BUTTON);
1202                         break;
1203
1204                 case KEY_CTRLED | KEY_DOWN:
1205                         sim_room_button_pressed(CUTSCENES_BUTTON);
1206                         break;
1207
1208                 case KEY_TAB:
1209                         if (Player->readyroom_listing_mode == MODE_CAMPAIGNS)
1210                                 Player->readyroom_listing_mode = MODE_MISSIONS;
1211                         else
1212                                 Player->readyroom_listing_mode = MODE_CAMPAIGNS;
1213
1214                         Selected_line = Scroll_offset = 0;
1215                         gamesnd_play_iface(SND_USER_SELECT);
1216                         sim_room_build_listing();
1217                         break;
1218
1219                 case KEY_F2:
1220                         gamesnd_play_iface(SND_SWITCH_SCREENS);
1221                         gameseq_post_event(GS_EVENT_OPTIONS_MENU);
1222                         break;
1223         }       // end switch
1224
1225         for (i=0; i<NUM_BUTTONS; i++){
1226                 if (Buttons[gr_screen.res][i].button.pressed()){
1227                         if (sim_room_button_pressed(i)){
1228                                 return;
1229                         }
1230                 }
1231         }
1232
1233         for (i=0; i<LIST_BUTTONS_MAX; i++) {
1234                 if (List_buttons[i].is_mouse_on())
1235                         select_tease_line = i + Scroll_offset;
1236         
1237                 if (List_buttons[i].pressed()) {
1238                         Selected_line = i + Scroll_offset;
1239                         gamesnd_play_iface(SND_USER_SELECT);
1240                 }
1241         }
1242
1243         GR_MAYBE_CLEAR_RES(Background_bitmap);
1244         if (Background_bitmap >= 0) {
1245                 gr_set_bitmap(Background_bitmap);
1246                 gr_bitmap(0, 0);
1247         }
1248
1249         Ui_window.draw();
1250
1251         for (i=TECH_DATABASE_BUTTON; i<=CREDITS_BUTTON; i++){
1252                 if (Buttons[gr_screen.res][i].button.button_down()){
1253                         break;
1254                 }
1255         }
1256
1257         if (i > CREDITS_BUTTON){
1258                 Buttons[gr_screen.res][SIMULATOR_BUTTON].button.draw_forced(2);
1259         }
1260
1261         if (!Buttons[gr_screen.res][CAMPAIGN_TAB].button.button_down() && !Buttons[gr_screen.res][MISSION_TAB].button.button_down()) {
1262                 if (Player->readyroom_listing_mode == MODE_CAMPAIGNS){
1263                         Buttons[gr_screen.res][CAMPAIGN_TAB].button.draw_forced(2);
1264                 } else if (Player->readyroom_listing_mode == MODE_MISSIONS){
1265                         Buttons[gr_screen.res][MISSION_TAB].button.draw_forced(2);
1266                 }
1267         }
1268
1269         gr_set_font(FONT1);
1270         if (Player->readyroom_listing_mode == MODE_CAMPAIGNS) {
1271                 gr_set_color_fast(&Color_text_heading);
1272                 strcpy(buf, Campaign.name);
1273                 gr_force_fit_string(buf, 255, list_w1);
1274                 gr_printf(list_x1, Mission_list_coords[gr_screen.res][1], buf);
1275
1276                 if (Campaign.filename) {                        
1277                         sprintf(buf, NOX("%s%s"), Campaign.filename, FS_CAMPAIGN_FILE_EXT);
1278                         gr_force_fit_string(buf, 255, list_w2);
1279                         gr_printf(list_x2, Mission_list_coords[gr_screen.res][1], buf);         
1280
1281                         // blit the proper icons if necessary
1282                         char full_name[256];
1283                         memset(full_name, 0, 256);
1284                         strcpy(full_name, cf_add_ext(Campaign.filename,FS_CAMPAIGN_FILE_EXT));
1285                         fs_builtin_mission *fb = game_find_builtin_mission(full_name);
1286                         if(fb != NULL){
1287                                 // sim_room_blit_icons(0, Mission_list_coords[gr_screen.res][1], fb, 0);
1288                         }
1289                 }
1290         }
1291
1292         line = Scroll_offset;
1293         while (sim_room_line_query_visible(line)) {
1294                 y = list_y + sim_room_lines[line].y - sim_room_lines[Scroll_offset].y;
1295
1296                 if (sim_room_lines[line].type != READYROOM_LINE_CAMPAIGN) {
1297                         List_buttons[line - Scroll_offset].update_dimensions(list_x1, y, list_x2 + list_w2 - list_x1, font_height);
1298                         List_buttons[line - Scroll_offset].enable();
1299
1300                 } else
1301                         List_buttons[line - Scroll_offset].disable();
1302
1303                 if (line == Selected_line)
1304                         gr_set_color_fast(&Color_text_selected);
1305                 else if (line == select_tease_line)
1306                         gr_set_color_fast(&Color_text_subselected);
1307                 else
1308                         gr_set_color_fast(&Color_text_normal);
1309
1310                 strcpy(buf, sim_room_lines[line].name);
1311                 gr_force_fit_string(buf, 255, list_x1 + list_w1 - sim_room_lines[line].x);
1312                 gr_printf(sim_room_lines[line].x, y, buf);
1313
1314                 if (sim_room_lines[line].filename) {
1315                         strcpy(buf, sim_room_lines[line].filename);
1316                         gr_force_fit_string(buf, 255, list_w2);
1317                         gr_printf(list_x2, y, buf);
1318                 }
1319
1320                 // blit additional icon information
1321                 sim_room_blit_icons(line, y);
1322
1323                 line++;
1324         }
1325
1326         i = line - Scroll_offset;
1327         while (i < LIST_BUTTONS_MAX)
1328                 List_buttons[i++].disable();
1329
1330         // blit help overlay if active
1331         help_overlay_maybe_blit(SIM_ROOM_OVERLAY);
1332
1333         gr_flip();
1334 }
1335
1336 void sim_room_blit_icons(int line_index, int y_start, fs_builtin_mission *fb, int is_md)
1337 {
1338         int is_from_volition = 0;       
1339
1340         // determine icon status
1341         if(fb == NULL){
1342                 is_from_volition = (sim_room_lines[line_index].flags & READYROOM_FLAG_FROM_VOLITION) ? 1 : 0;           
1343         } else {
1344                 is_from_volition = (fb->flags & FSB_FROM_VOLITION) ? 1 : 0;             
1345         }
1346
1347         // if the line is flagged as a volition file
1348         if(is_from_volition && (Mission_icon_bitmaps[MISSION_ICON_VOLITION] >= 0)){             
1349                 gr_set_bitmap(Mission_icon_bitmaps[MISSION_ICON_VOLITION]);
1350                 gr_bitmap(Sim_volition_icon_x[gr_screen.res], y_start + MISSION_ICON_VOLITION_Y_OFFSET);
1351         }       
1352 }
1353
1354 ///  Campaign room stuff below
1355 int Cr_list_coords[GR_NUM_RESOLUTIONS][4] = {
1356         { // GR_640
1357                 47, 21, 565, 233
1358         },
1359         { // GR_1024
1360                 64, 34, 916, 373
1361         }
1362 };
1363
1364 int Cr_info_coords[GR_NUM_RESOLUTIONS][4] = {
1365         { // GR_640
1366                 28, 267, 476, 103
1367         },
1368         { // GR_1024
1369                 45, 427, 761, 165
1370         },
1371 };
1372
1373 #define CR_NUM_BUTTONS                                  6
1374
1375 #define CR_SCROLL_UP_BUTTON                     0
1376 #define CR_SCROLL_DOWN_BUTTON                   1
1377 #define CR_SCROLL_INFO_UP_BUTTON                2
1378 #define CR_SCROLL_INFO_DOWN_BUTTON      3
1379 #define CR_RESET_BUTTON                                 4
1380 #define CR_COMMIT_BUTTON                                5
1381
1382 #define MAX_INFO_LINES          20
1383
1384 #define MAX_INFO_LINE_LEN       256
1385
1386 ui_button_info Cr_buttons[GR_NUM_RESOLUTIONS][CR_NUM_BUTTONS] = {
1387         { // GR_640
1388                 ui_button_info("CAB_00",        2,              42,     -1,     -1,     0),
1389                 ui_button_info("CAB_01",        2,              89,     -1,     -1,     1),
1390                 ui_button_info("CAB_02",        2,              279,    -1,     -1,     2),
1391                 ui_button_info("CAB_03",        2,              325,    -1,     -1,     3),
1392                 ui_button_info("CAB_04",        579,    353,    -1,     -1,     4),
1393                 ui_button_info("CAB_05",        575,    434,    -1,     -1,     5),
1394         },
1395         { // GR_1024
1396                 ui_button_info("2_CAB_00",      3,              68,     -1,     -1,     0),
1397                 ui_button_info("2_CAB_01",      3,              142,    -1,     -1,     1),
1398                 ui_button_info("2_CAB_02",      3,              446,    -1,     -1,     2),
1399                 ui_button_info("2_CAB_03",      3,              520,    -1,     -1,     3),
1400                 ui_button_info("2_CAB_04",      927,    565,    -1,     -1,     4),
1401                 ui_button_info("2_CAB_05",      920,    694,    -1,     -1,     5),
1402         }
1403 };
1404
1405 // text
1406 #define CR_NUM_TEXT                     3
1407 UI_XSTR Cr_text[GR_NUM_RESOLUTIONS][CR_NUM_TEXT] = {
1408         { // GR_640
1409                 { "Restart",            1403,           569,    326, UI_XSTR_COLOR_GREEN,       -1, &Cr_buttons[0][CR_RESET_BUTTON].button },
1410                 { "Campaign",           1404,           569,    337, UI_XSTR_COLOR_GREEN,       -1, &Cr_buttons[0][CR_RESET_BUTTON].button },
1411                 { "Select",                     1409,           568,    413, UI_XSTR_COLOR_PINK,        -1, &Cr_buttons[0][CR_COMMIT_BUTTON].button },
1412         },
1413         { // GR_1024
1414                 { "Restart",            1403,           922,    523, UI_XSTR_COLOR_GREEN,       -1, &Cr_buttons[1][CR_RESET_BUTTON].button },
1415                 { "Campaign",           1404,           922,    538, UI_XSTR_COLOR_GREEN,       -1, &Cr_buttons[1][CR_RESET_BUTTON].button },
1416                 { "Select",                     1409,           921,    665, UI_XSTR_COLOR_PINK,        -1, &Cr_buttons[1][CR_COMMIT_BUTTON].button },
1417         }
1418 };
1419
1420 static struct {
1421         char *text;
1422         int len;
1423 } campaign_desc_lines[MAX_DESC_LINES];
1424
1425 static int Num_desc_lines;
1426 static int Desc_scroll_offset;
1427 static int Selected_campaign_index;
1428 static int Active_campaign_index;
1429
1430 char *Info_text_ptrs[MAX_INFO_LINES];
1431 int Num_info_lines, Info_text_line_size[MAX_INFO_LINES];
1432
1433 void campaign_room_build_listing()
1434 {
1435         int c, i, y, type, max_players; 
1436         int font_height = gr_get_font_height();
1437         char name[NAME_LENGTH];
1438
1439         Num_lines = y = 0;
1440         if (!Campaign_names_inited) {
1441                 for (i=0; i<Num_campaigns; i++) {
1442                         Campaign_names[i] = NULL;
1443                         if ( mission_campaign_get_info(Campaign_file_names[i], name, &type, &max_players) ) {
1444 #ifdef PD_BUILD
1445                                 if((game_find_builtin_mission(name) == NULL) && !strstr(name, "peterdrake")){
1446                                         continue;
1447                                 } else {
1448                                         Campaign_names[i] = strdup(name);
1449                                 }
1450 #else
1451                                 if (type == CAMPAIGN_TYPE_SINGLE){
1452                                         Campaign_names[i] = strdup(name);
1453                                 }
1454 #endif
1455                         }
1456                 }
1457
1458                 Campaign_names_inited = 1;
1459         }
1460
1461         for (c=0; c<Num_campaigns; c++) {
1462                 if (Campaign_names[c]) {
1463
1464                         // determine some extra information
1465                         int flags = 0;
1466                         fs_builtin_mission *fb = game_find_builtin_mission(Campaign_file_names[c]);
1467                         if(fb != NULL){
1468                                 if(fb->flags & FSB_FROM_VOLITION){
1469                                         flags |= READYROOM_FLAG_FROM_VOLITION;
1470                                 }                               
1471                         }
1472
1473                         sim_room_line_add(READYROOM_LINE_CAMPAIGN, Campaign_names[c], Campaign_file_names[c], Cr_list_coords[gr_screen.res][0], y, flags);
1474                         y += font_height + 2;
1475                 }
1476         }
1477 }
1478
1479 void set_new_campaign_line(int n)
1480 {
1481         char *str;
1482
1483         Selected_campaign_index = n;
1484         str = Campaign_descs[Selected_campaign_index];
1485         Num_info_lines = 0;
1486         if (str) {
1487                 Num_info_lines = split_str(str, Cr_info_coords[gr_screen.res][2], Info_text_line_size, Info_text_ptrs, MAX_INFO_LINES);
1488                 Assert(Num_info_lines >= 0);
1489         }
1490
1491         Desc_scroll_offset = 0;
1492 }
1493
1494 void campaign_room_scroll_info_up()
1495 {
1496         if (Desc_scroll_offset) {
1497                 Desc_scroll_offset--;
1498                 gamesnd_play_iface(SND_SCROLL);
1499
1500         } else
1501                 gamesnd_play_iface(SND_GENERAL_FAIL);
1502 }
1503
1504 void campaign_room_scroll_info_down()
1505 {
1506         if ( (Num_info_lines - Desc_scroll_offset) * gr_get_font_height() > Cr_info_coords[gr_screen.res][3]) {
1507                 Desc_scroll_offset++;
1508                 gamesnd_play_iface(SND_SCROLL);
1509
1510         } else
1511                 gamesnd_play_iface(SND_GENERAL_FAIL);
1512 }
1513
1514 // returns: 0 = success, !0 = aborted or failed
1515 int campaign_room_reset_campaign(int n)
1516 {
1517         char *filename;
1518         int z;
1519
1520         // 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), Campaign_names[n]);
1521         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));
1522         if (z == 1) {
1523                 filename = (char *) malloc(strlen(Campaign_file_names[n]) + 5);
1524                 strcpy(filename, Campaign_file_names[n]);
1525                 strcat(filename, FS_CAMPAIGN_FILE_EXT);
1526
1527                 mission_campaign_savefile_delete(filename);
1528                 mission_campaign_load(filename);
1529                 mission_campaign_next_mission();
1530                 return 0;
1531         }
1532
1533         return 1;
1534 }
1535
1536 void campaign_room_commit()
1537 {
1538         if (Selected_campaign_index < 0) {
1539                 gamesnd_play_iface(SND_GENERAL_FAIL);
1540                 return;
1541         }
1542
1543         if (stricmp(Campaign_file_names[Selected_campaign_index], Campaign.filename)) {  // new campaign selected
1544                 if ((Active_campaign_index >= 0) && campaign_room_reset_campaign(Active_campaign_index)) {
1545                         gamesnd_play_iface(SND_GENERAL_FAIL);
1546                         return;
1547                 }
1548
1549                 mission_campaign_savefile_delete(Campaign_file_names[Selected_campaign_index]);
1550                 mission_campaign_load(Campaign_file_names[Selected_campaign_index]);
1551                 strcpy(Player->current_campaign, Campaign.filename);  // track new campaign for player
1552         }
1553
1554         if (mission_campaign_next_mission()) {  // is campaign and next mission valid?
1555                 gamesnd_play_iface(SND_GENERAL_FAIL);
1556                 return;
1557         }
1558
1559         gameseq_post_event(GS_EVENT_MAIN_MENU);
1560         gamesnd_play_iface(SND_COMMIT_PRESSED);
1561 }
1562
1563 int campaign_room_button_pressed(int n)
1564 {
1565         switch (n) {
1566                 case CR_SCROLL_UP_BUTTON:
1567                         sim_room_scroll_screen_up();
1568                         break;
1569
1570                 case CR_SCROLL_DOWN_BUTTON:
1571                         sim_room_scroll_screen_down();
1572                         break;
1573
1574                 case CR_SCROLL_INFO_UP_BUTTON:
1575                         campaign_room_scroll_info_up();
1576                         break;
1577
1578                 case CR_SCROLL_INFO_DOWN_BUTTON:
1579                         campaign_room_scroll_info_down();
1580                         break;
1581
1582                 case CR_COMMIT_BUTTON:
1583                         campaign_room_commit();
1584                         break;
1585
1586                 /*
1587                 case CR_HELP_BUTTON:
1588                         launch_context_help();
1589                         gamesnd_play_iface(SND_HELP_PRESSED);
1590                         break;
1591
1592                 case CR_OPTIONS_BUTTON:
1593                         gamesnd_play_iface(SND_SWITCH_SCREENS);
1594                         gameseq_post_event(GS_EVENT_OPTIONS_MENU);
1595                         return 1;
1596                 */
1597
1598                 case CR_RESET_BUTTON:
1599                         if ( (Active_campaign_index < 0) || (Active_campaign_index >= Num_campaigns) )
1600                                 gamesnd_play_iface(SND_GENERAL_FAIL);
1601                         else if (campaign_room_reset_campaign(Active_campaign_index))
1602                                 gamesnd_play_iface(SND_GENERAL_FAIL);
1603                         else
1604                                 gamesnd_play_iface(SND_USER_SELECT);
1605
1606                         break;
1607         }
1608
1609         return 0;
1610 }
1611
1612 void campaign_room_init()
1613 {
1614         int i, j, load_failed;
1615         ui_button_info *b;
1616         char wild_card[256];
1617
1618         list_h = Mission_list_coords[gr_screen.res][3];
1619
1620         // common_set_interface_palette("InterfacePalette");  // set the interface palette
1621         Ui_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0);
1622         Ui_window.set_mask_bmap(Campaign_mask_filename[gr_screen.res]);
1623
1624         for (i=0; i<CR_NUM_BUTTONS; i++) {
1625                 b = &Cr_buttons[gr_screen.res][i];
1626
1627                 b->button.create(&Ui_window, "", b->x, b->y, 60, 30, (i < 2), 1);
1628                 // set up callback for when a mouse first goes over a button
1629                 b->button.set_highlight_action(common_play_highlight_sound);
1630                 b->button.set_bmaps(b->filename);
1631                 b->button.link_hotspot(b->hotspot);
1632         }
1633
1634         for (i=0; i<LIST_BUTTONS_MAX; i++) {
1635                 List_buttons[i].create(&Ui_window, "", 0, 0, 60, 30, 0, 1);
1636                 List_buttons[i].hide();
1637                 List_buttons[i].disable();
1638         }
1639
1640         // add xstrs
1641         for(i=0; i<CR_NUM_TEXT; i++){
1642                 Ui_window.add_XSTR(&Cr_text[gr_screen.res][i]);
1643         }
1644
1645         // set up sim_rooms for buttons so we draw the correct animation frame when a key is pressed
1646         Cr_buttons[gr_screen.res][CR_SCROLL_UP_BUTTON].button.set_hotkey(KEY_PAGEUP);
1647         Cr_buttons[gr_screen.res][CR_SCROLL_DOWN_BUTTON].button.set_hotkey(KEY_PAGEDOWN);
1648         Cr_buttons[gr_screen.res][CR_RESET_BUTTON].button.set_hotkey(KEY_DELETE);
1649         Cr_buttons[gr_screen.res][CR_COMMIT_BUTTON].button.set_hotkey(KEY_CTRLED | KEY_ENTER);
1650         // Cr_buttons[gr_screen.res][CR_HELP_BUTTON].button.set_hotkey(KEY_F2);
1651
1652         Background_bitmap = bm_load(Campaign_filename[gr_screen.res]);
1653
1654         // load in help overlay bitmap  
1655         help_overlay_load(CAMPAIGN_ROOM_OVERLAY);
1656         help_overlay_set_state(CAMPAIGN_ROOM_OVERLAY,0);
1657
1658         Num_desc_lines = 0;
1659
1660         Desc_scroll_offset = Scroll_offset = 0;
1661         load_failed = mission_load_up_campaign();
1662         if (!load_failed)
1663                 mission_campaign_next_mission();
1664
1665         else {
1666                 Campaign.filename[0] = 0;
1667                 Campaign.num_missions = 0;
1668         }
1669
1670         Num_campaigns = 0;
1671
1672         Get_file_list_filter = campaign_room_campaign_filter;
1673         memset(wild_card, 0, 256);
1674         strcpy(wild_card, NOX("*"));
1675         strcat(wild_card, FS_CAMPAIGN_FILE_EXT);
1676         Num_campaigns = cf_get_file_list(MAX_CAMPAIGNS, Campaign_file_names, CF_TYPE_MISSIONS, wild_card, CF_SORT_NAME);
1677
1678         for (i=0; i<Num_campaigns; i++) {
1679                 for (j=0; j<Num_campaigns; j++) {
1680                         if (Campaign_file_names_temp[j]) {
1681                                 if (!strncmp(Campaign_file_names[i], Campaign_file_names_temp[j], strlen(Campaign_file_names_temp[j]) - 4)) {
1682                                         Campaign_descs[i] = Campaign_descs_temp[j];
1683                                         free(Campaign_file_names_temp[j]);
1684                                         Campaign_file_names_temp[j] = NULL;
1685                                         break;
1686                                 }
1687                         }
1688                 }
1689
1690                 Assert(j < Num_campaigns);  // Campaign not found?  How is that possible?
1691         }
1692
1693         Campaign_names_inited = 0;
1694         campaign_room_build_listing();
1695
1696         Selected_campaign_index = Active_campaign_index = -1;
1697         if (!load_failed) {
1698                 for (i=0; i<Num_campaigns; i++)
1699                         if (!stricmp(Campaign_file_names[i], Campaign.filename)) {
1700                                 set_new_campaign_line(i);
1701                                 Active_campaign_index = i;
1702                                 break;
1703                         }
1704         }
1705 }
1706
1707 void campaign_room_close()
1708 {
1709         int i;
1710
1711         for (i=0; i<Num_campaigns; i++)
1712                 if (Campaign_descs[i])
1713                         free(Campaign_descs[i]);
1714
1715         if (Background_bitmap >= 0)
1716                 bm_unload(Background_bitmap);
1717
1718         if (Campaign_names_inited)
1719                 for (i=0; i<Num_campaigns; i++)
1720                         if (Campaign_names[i])
1721                                 free(Campaign_names[i]);
1722
1723         for (i=0; i<Num_campaigns; i++)
1724                 free(Campaign_file_names[i]);
1725
1726         // unload the overlay bitmap
1727         help_overlay_unload(CAMPAIGN_ROOM_OVERLAY);
1728
1729         Ui_window.destroy();
1730         common_free_interface_palette();                // restore game palette
1731         write_pilot_file();
1732 }
1733
1734 void campaign_room_do_frame(float frametime)
1735 {
1736         char buf[256];
1737         char line_text[MAX_INFO_LINE_LEN];
1738         int i, k, y, line;
1739         int font_height = gr_get_font_height();
1740         int select_tease_line = -1;  // line mouse is down on, but won't be selected until button released
1741
1742         if ( help_overlay_active(CAMPAIGN_ROOM_OVERLAY) ) {
1743                 // Cr_buttons[gr_screen.res][CR_HELP_BUTTON].button.reset_status();
1744                 Ui_window.set_ignore_gadgets(1);
1745         }
1746
1747         k = Ui_window.process() & ~KEY_DEBUGGED;
1748
1749         if ( (k > 0) || B1_JUST_RELEASED ) {
1750                 if ( help_overlay_active(CAMPAIGN_ROOM_OVERLAY) ) {
1751                         help_overlay_set_state(CAMPAIGN_ROOM_OVERLAY, 0);
1752                         Ui_window.set_ignore_gadgets(0);
1753                         k = 0;
1754                 }
1755         }
1756
1757         if ( !help_overlay_active(CAMPAIGN_ROOM_OVERLAY) ) {
1758                 Ui_window.set_ignore_gadgets(0);
1759         }
1760
1761         switch (k) {
1762                 case KEY_DOWN:  // scroll list down
1763                         if (Selected_campaign_index < Num_campaigns - 1) {
1764                                 set_new_campaign_line(Selected_campaign_index + 1);
1765                                 gamesnd_play_iface(SND_SCROLL);
1766
1767                         } else
1768                                 gamesnd_play_iface(SND_GENERAL_FAIL);
1769
1770                         break;
1771
1772                 case KEY_UP:  // scroll list up
1773                         if (Selected_campaign_index < 0)
1774                                 Selected_campaign_index = 1;
1775
1776                         if (Selected_campaign_index) {
1777                                 set_new_campaign_line(Selected_campaign_index - 1);
1778                                 gamesnd_play_iface(SND_SCROLL);
1779
1780                         } else
1781                                 gamesnd_play_iface(SND_GENERAL_FAIL);
1782
1783                         break;
1784
1785                 case KEY_ESC:
1786                         gameseq_post_event(GS_EVENT_MAIN_MENU);
1787                         break;
1788         }       // end switch
1789
1790         for (i=0; i<CR_NUM_BUTTONS; i++){
1791                 if (Cr_buttons[gr_screen.res][i].button.pressed()){
1792                         if (campaign_room_button_pressed(i)){
1793                                 return;
1794                         }
1795                 }
1796         }
1797
1798         for (i=0; i<LIST_BUTTONS_MAX; i++) {
1799                 if (List_buttons[i].is_mouse_on()){
1800                         select_tease_line = i + Scroll_offset;
1801                 }
1802         
1803                 if (List_buttons[i].pressed()) {
1804                         set_new_campaign_line(i + Scroll_offset);
1805                         gamesnd_play_iface(SND_USER_SELECT);
1806                 }
1807         }
1808
1809         GR_MAYBE_CLEAR_RES(Background_bitmap);
1810         if (Background_bitmap >= 0) {
1811                 gr_set_bitmap(Background_bitmap);
1812                 gr_bitmap(0, 0);
1813         }
1814
1815         Ui_window.draw();
1816
1817         gr_set_font(FONT1);
1818         line = Scroll_offset;
1819         while (sim_room_line_query_visible(line)) {
1820                 y = Cr_list_coords[gr_screen.res][1] + sim_room_lines[line].y - sim_room_lines[Scroll_offset].y;
1821
1822                 List_buttons[line - Scroll_offset].update_dimensions(Cr_list_coords[gr_screen.res][0], y, Cr_list_coords[gr_screen.res][2], font_height);
1823                 List_buttons[line - Scroll_offset].enable();
1824
1825                 if (!stricmp(sim_room_lines[line].filename, Campaign.filename)) {
1826                         gr_set_color_fast(&Color_white);
1827                         i = y + font_height / 2 - 1;
1828                         gr_circle(Cr_list_coords[gr_screen.res][0] - 6, i, 5);
1829
1830                         gr_set_color_fast(&Color_bright_white);
1831                         gr_line(Cr_list_coords[gr_screen.res][0] - 10, i, Cr_list_coords[gr_screen.res][0] - 8, i);
1832                         gr_line(Cr_list_coords[gr_screen.res][0] - 6, i - 4, Cr_list_coords[gr_screen.res][0] - 6, i - 2);
1833                         gr_line(Cr_list_coords[gr_screen.res][0] - 4, i, Cr_list_coords[gr_screen.res][0] - 2, i);
1834                         gr_line(Cr_list_coords[gr_screen.res][0] - 6, i + 2, Cr_list_coords[gr_screen.res][0] - 6, i + 4);
1835                 }
1836
1837                 if (line == Selected_campaign_index)
1838                         gr_set_color_fast(&Color_text_selected);
1839                 else if (line == select_tease_line)
1840                         gr_set_color_fast(&Color_text_subselected);
1841                 else
1842                         gr_set_color_fast(&Color_text_normal);
1843
1844                 strcpy(buf, sim_room_lines[line].name);
1845                 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);
1846                 gr_printf(sim_room_lines[line].x, y, buf);
1847                 line++;
1848         }
1849
1850         i = line - Scroll_offset;
1851         while (i < LIST_BUTTONS_MAX)
1852                 List_buttons[i++].disable();
1853
1854         y = 0;
1855         i = Desc_scroll_offset;
1856         gr_set_color_fast(&Color_text_normal);
1857
1858         while (y + font_height <= Cr_info_coords[gr_screen.res][3]) {
1859                 if (i >= Num_info_lines)
1860                         break;
1861
1862                 Assert(Info_text_line_size[i] < MAX_INFO_LINE_LEN);
1863                 strncpy(line_text, Info_text_ptrs[i], Info_text_line_size[i]);
1864                 line_text[Info_text_line_size[i]] = 0;
1865                 drop_white_space(line_text);
1866                 gr_string(Cr_info_coords[gr_screen.res][0], Cr_info_coords[gr_screen.res][1] + y, line_text);
1867                 y += font_height;
1868                 i++;
1869         }
1870
1871         // blit help overlay if active
1872         help_overlay_maybe_blit(CAMPAIGN_ROOM_OVERLAY);
1873
1874         gr_flip();
1875 }
1876
1877 void sim_room_load_mission_icons()
1878 {
1879         int idx;
1880         
1881         // load all bitmaps
1882         for(idx=0; idx<NUM_MISSION_ICONS; idx++){
1883                 Mission_icon_bitmaps[idx] = -1;
1884                 Mission_icon_bitmaps[idx] = bm_load(Mission_icon_bitmap_filenames[idx]);
1885         }
1886 }
1887
1888 void sim_room_unload_mission_icons()
1889 {
1890         int idx;
1891
1892         // unload all bitmaps
1893         for(idx=0; idx<NUM_MISSION_ICONS; idx++){
1894                 if(Mission_icon_bitmaps[idx] >= 0){
1895                         bm_unload(Mission_icon_bitmaps[idx]);
1896                         Mission_icon_bitmaps[idx] = -1;
1897                 }
1898         }
1899 }
1900