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