]> icculus.org git repositories - taylor/freespace2.git/blob - src/mission/missionhotkey.cpp
Initial revision
[taylor/freespace2.git] / src / mission / missionhotkey.cpp
1 /*
2  * $Logfile: /Freespace2/code/Mission/MissionHotKey.cpp $
3  * $Revision$
4  * $Date$
5  * $Author$
6  *
7  * C module for the Hotkey selection screen
8  *
9  * $Log$
10  * Revision 1.1  2002/05/03 03:28:10  root
11  * Initial revision
12  *
13  * 
14  * 5     10/14/99 2:50p Jefff
15  * localization fixes
16  * 
17  * 4     8/17/99 3:00p Jefff
18  * updated for fs2
19  * 
20  * 3     10/13/98 9:28a Dave
21  * Started neatening up freespace.h. Many variables renamed and
22  * reorganized. Added AlphaColors.[h,cpp]
23  * 
24  * 2     10/07/98 10:53a Dave
25  * Initial checkin.
26  * 
27  * 1     10/07/98 10:49a Dave
28  * 
29  * 54    6/09/98 5:15p Lawrance
30  * French/German localization
31  * 
32  * 53    6/09/98 10:31a Hoffoss
33  * Created index numbers for all xstr() references.  Any new xstr() stuff
34  * added from here on out should be added to the end if the list.  The
35  * current list count can be found in FreeSpace.cpp (search for
36  * XSTR_SIZE).
37  * 
38  * 52    5/26/98 11:10a Lawrance
39  * Fix bug where window controls get disabled when F1 pressed twice
40  * 
41  * 51    5/06/98 10:47a Allender
42  * allow escape pods to be shown on hotkey screen
43  * 
44  * 50    5/05/98 1:49a Lawrance
45  * Add in missing help overlays
46  * 
47  * 49    4/25/98 7:40p Allender
48  * fixd some small hotkey stuff.  Worked on turret orientation being
49  * correct for multiplayer.  new sexpression called end-campaign will will
50  * end the main campaign
51  * 
52  * 48    4/20/98 12:36a Mike
53  * Make team vs. team work when player is hostile.  Several targeting
54  * problems.
55  * 
56  * 47    4/13/98 10:52a Jasen
57  * Updated coords for new Hotkey config screen.
58  * 
59  * 46    3/11/98 10:33p Allender
60  * made a "hidden" hotkey which is the last hotkey in the set, which means
61  * to hide the ship/wing until the mission is entered.
62  * 
63  * 45    3/11/98 11:20a Hoffoss
64  * Changed hotkey screen to only show friendly and hostile  catagories
65  * (everything non-friendly is hostile).
66  * 
67  * 44    3/10/98 9:51a Allender
68  * minor fixups to hotkey stuff.
69  * 
70  * 43    3/07/98 8:09p Allender
71  * maybe restore hotkeys when setting defaults
72  * 
73  * 42    2/27/98 4:31p Allender
74  * don't show ships on hotkey screen which are hidden to sensors
75  * 
76  * 41    2/23/98 8:14a John
77  * 
78  * 40    2/23/98 8:06a John
79  * Externalized some strings
80  * 
81  * 39    2/22/98 12:19p John
82  * Externalized some strings
83  * 
84  * 38    2/10/98 2:06p Hoffoss
85  * Eliminated cargo (and NavBouys) from hotkey list.
86  * 
87  * 37    1/29/98 10:26a Hoffoss
88  * Made changes so arrow buttons repeat scrolling when held down.
89  * 
90  * 36    1/28/98 6:22p Dave
91  * Made standalone use ~8 megs less memory. Fixed multiplayer submenu
92  * sequencing bug.
93  * 
94  * 35    1/26/98 4:42p Allender
95  * fixed restoration of hotkeys when replaying mission.  Change the
96  * meaning of "departed wing" to mean anytime a wing "departs" (with any
97  * number of remaining wingmen).
98  * 
99  * 34    1/19/98 9:37p Allender
100  * Great Compiler Warning Purge of Jan, 1998.  Used pragma's in a couple
101  * of places since I was unsure of what to do with code.
102  * 
103  * 33    1/18/98 5:09p Lawrance
104  * Added support for TEAM_TRAITOR
105  * 
106  * 32    1/14/98 5:22p Allender
107  * save/restore hotkey selections when replaying the same mission
108  * 
109  * 31    12/15/97 12:13p Hoffoss
110  * Changed code to allow hotkey listing to repeat scroll when mouse held
111  * down on buttons.
112  * 
113  * 30    12/10/97 2:30p Hoffoss
114  * Removed dead code that isn't being used anymore.
115  * 
116  * 29    12/09/97 8:12a Allender
117  * changes to hotkey stuff.  Don't allow mission defined hotkeys to
118  * override user defined ones once the mission starts
119  * 
120  * 28    12/03/97 4:16p Hoffoss
121  * Changed sound stuff used in interface screens for interface purposes.
122  * 
123  * 27    12/01/97 3:39p Hoffoss
124  * Changed naming of headings.
125  * 
126  * 26    12/01/97 3:29p Jasen
127  * Fixed button coordinates.
128  * 
129  * 25    12/01/97 2:50p Hoffoss
130  * Improved hotkey screen.  F keys are in seperate columns now, and
131  * Shift-F key adds that to item.
132  * 
133  * 24    11/24/97 10:14p Allender
134  * fixed a couple of problem with assignments in the hotkey screen.  alpha
135  * wing problems, num_ships problems
136  * 
137  * 23    11/19/97 3:40p Allender
138  * don't allow player to get assigned by himself to a hotkey.  Don't allow
139  * navbuoys to be assigned either
140  * 
141  * 22    11/10/97 5:36p Hoffoss
142  * Fixed bug in last fix. :)
143  *
144  * $NoKeywords: $
145  */
146
147 #include "missionhotkey.h"
148 #include "gamesequence.h"
149 #include "freespace.h"
150 #include "key.h"
151 #include "bmpman.h"
152 #include "2d.h"
153 #include "timer.h"
154 #include "gamesnd.h"
155 #include "audiostr.h"
156 #include "ship.h"
157 #include "object.h"
158 #include "linklist.h"
159 #include "hudtarget.h"
160 #include "player.h"
161 #include "ui.h"
162 #include "uidefs.h"
163 #include "missionscreencommon.h"
164 #include "font.h"
165 #include "gamesnd.h"
166 #include "controlsconfig.h"
167 #include "contexthelp.h"
168 #include "alphacolors.h"
169 #include "beam.h"
170
171 static int Key_sets[MAX_KEYED_TARGETS] = {
172         KEY_F5,
173         KEY_F6,
174         KEY_F7,
175         KEY_F8,
176         KEY_F9,
177         KEY_F10,
178         KEY_F11,
179         KEY_F12
180 };
181
182 /////////////////////////////
183
184 static int Hotkey_bits[MAX_SHIPS];  // bitfield indicating which hotkeys are used by each ship
185
186 static int Hotkey_sets_saved;                   // have we saved the sets for this mission
187
188 static int Mission_hotkey_save_timestamp;               // timestamp used to tell us when we can save
189 #define HOTKEY_SAVE_TIME                                15000           // save sets this number of milliseconds into the mission
190
191 typedef struct {
192         int setnum;
193         char name[NAME_LENGTH];
194 } HK_save_info;
195
196 HK_save_info Hotkey_saved_info[MAX_HOTKEY_TARGET_ITEMS];
197 int Num_hotkeys_saved;
198
199
200 static char *Hotkey_background_fname[GR_NUM_RESOLUTIONS] = {
201         "Hotkeys",              // GR_640
202         "2_Hotkeys"             // GR_1024
203 };
204
205 static char *Hotkey_mask_fname[GR_NUM_RESOLUTIONS] = {
206         "Hotkeys-M",            // GR_640
207         "2_Hotkeys-M"   // GR_1024
208 };
209
210 //#define GROUP_LIST_X  40
211 //#define GROUP_LIST_W  160
212
213 // #define ICON_LIST_X  219
214 // #define ICON_LIST_W  8
215
216 // #define ICON_LIST_X  280
217 // #define ICON_LIST_W  8
218
219 //#define SHIP_LIST_X   242
220 //#define SHIP_LIST_X2  259
221 //#define SHIP_LIST_W   341
222 //#define SHIP_LIST_W2  324
223
224 // #define SHIP_LIST_X  302
225 // #define SHIP_LIST_X2 319
226 // #define SHIP_LIST_W  281
227 // #define SHIP_LIST_W2 264
228
229 // #define LIST_Y                       70
230 // #define LIST_H                       280
231
232 /*
233 #define HOTKEY_X                575
234 #define HOTKEY_Y                41
235 */
236  
237 #define HOTKEY_LINE_HEADING     1
238 #define HOTKEY_LINE_WING                2
239 #define HOTKEY_LINE_SHIP                3
240 #define HOTKEY_LINE_SUBSHIP     4  // ship that is in a wing
241
242 #define WING_FLAG       0x80000
243
244 #define MAX_LINES                                       200
245 #define NUM_BUTTONS                             10
246 #define LIST_BUTTONS_MAX                40
247
248 #define SCROLL_UP_BUTTON                0
249 #define SCROLL_DOWN_BUTTON              1
250 #define CANCEL_BUTTON                   2
251 #define CLEAR_BUTTON                            3
252 #define RESET_BUTTON                            4
253 #define ADD_HOTKEY_BUTTON               5
254 #define REMOVE_HOTKEY_BUTTON    6
255 #define HELP_BUTTON                             7
256 #define OPTIONS_BUTTON                  8
257 #define ACCEPT_BUTTON                   9
258
259 // coords for entire ship box
260 static int Hotkey_list_coords[GR_NUM_RESOLUTIONS][4] = {
261         {
262                 // GR_640
263                 29,                     // x
264                 22,                     // y
265                 502,                    // w
266                 315                     // h
267         },
268         {
269                 // GR_1024
270                 47,                     // x
271                 35,                     // y
272                 802,                    // w
273                 505                     // h
274         }
275 };
276
277 // coords for big "F9" thing in the corner
278 static int Hotkey_function_name_coords[GR_NUM_RESOLUTIONS][4] = {
279         {
280                 // GR_640
281                 570,                    // x
282                 14,                     // y
283                 59,                     // w
284                 22                              // h
285         },
286         {
287                 // GR_1024
288                 912,                    // x
289                 22,                     // y
290                 94,                     // w
291                 36                              // h
292         }
293 };
294
295 /*
296 #define FIELD_LEFT_EDGE         0
297 #define FIELD_F5                                1
298 #define FIELD_F6                                2
299 #define FIELD_F7                                3
300 #define FIELD_F8                                4
301 #define FIELD_F9                                5
302 #define FIELD_F10                               6
303 #define FIELD_F11                               7
304 #define FIELD_F12                               8
305 #define FIELD_ICON                      9
306 #define FIELD_RIGHT_EDGE        10
307 // x coords of unseen field boundaries (  | field1 | field2 | ... |  )
308 // entried will all be centered in fields except FIELD_SHIP which will be left justified
309 // an edge is named by the field on its left
310 static int Hotkey_field_edge[GR_NUM_RESOLUTIONS][11] = {
311         {
312                 29, 56, 83, 110, 137, 164, 191, 218, 245, 280, 531
313         },
314         {
315                 47, 91, 135, 179, 223, 267, 311, 355, 399, 448, 849
316         }
317 }
318 */
319
320 static int Hotkey_function_field_width[GR_NUM_RESOLUTIONS] = {
321         27,                     // GR_640
322         44                              // GR_1024
323 };
324 static int Hotkey_wing_icon_x[GR_NUM_RESOLUTIONS] = {
325         246,                    // GR_640
326         400                     // GR_1024
327 };
328 static int Hotkey_ship_x[GR_NUM_RESOLUTIONS] = {
329         280,                    // GR_640
330         448                     // GR_1024
331 };
332
333 // pragma pair put into place because of compiler warnings about being unable to inline
334 // the constructor function of the hotkey_buttons set.
335 #pragma warning(disable: 4710)
336
337 struct hotkey_buttons {
338         char *filename;
339         int x, y;
340         int hotspot;
341         UI_BUTTON button;  // because we have a class inside this struct, we need the constructor below..
342
343         hotkey_buttons(char *name, int x1, int y1, int h) : filename(name), x(x1), y(y1), hotspot(h) {}
344 };
345
346 // button definitions
347 static hotkey_buttons Buttons[GR_NUM_RESOLUTIONS][NUM_BUTTONS] = {
348 //XSTR:OFF
349         {
350                 // GR_640
351                 hotkey_buttons("HKB_00",        1,              94,     0),
352                 hotkey_buttons("HKB_01",        1,              133,    1),
353                 hotkey_buttons("HKB_02",        15,     342,    2),
354                 hotkey_buttons("HKB_03",        84,     342,    3),
355                 hotkey_buttons("HKB_04",        161,    342,    4),
356                 hotkey_buttons("HKB_05",        539,    5,              5),
357                 hotkey_buttons("HKB_06",        539,    44,     6),
358                 hotkey_buttons("HKB_07",        539,    431,    7),
359                 hotkey_buttons("HKB_08",        539,    455,    8),
360                 hotkey_buttons("HKB_09",        575,    432,    9)
361         },
362         {
363                 // GR_1024
364                 hotkey_buttons("2_HKB_00",              2,              150,    0),
365                 hotkey_buttons("2_HKB_01",              2,              213,    1),
366                 hotkey_buttons("2_HKB_02",              24,     548,    2),
367                 hotkey_buttons("2_HKB_03",              135,    548,    3),
368                 hotkey_buttons("2_HKB_04",              258,    548,    4),
369                 hotkey_buttons("2_HKB_05",              862,    8,              5),
370                 hotkey_buttons("2_HKB_06",              862,    71,     6),
371                 hotkey_buttons("2_HKB_07",              863,    690,    7),
372                 hotkey_buttons("2_HKB_08",              862,    728,    8),
373                 hotkey_buttons("2_HKB_09",              920,    692,    9)
374         }
375 //XSTR:ON
376 };
377 #pragma warning(default: 4710)
378
379 #define HOTKEY_NUM_TEXT         6
380 static UI_XSTR Hotkey_text[GR_NUM_RESOLUTIONS][HOTKEY_NUM_TEXT] = {
381         { 
382                 // GR_640
383                 { "Cancel",             1516,   7,      392,            UI_XSTR_COLOR_GREEN, -1, &Buttons[GR_640][CANCEL_BUTTON].button },
384                 { "Clear",              1517,   85, 392,                UI_XSTR_COLOR_GREEN, -1, &Buttons[GR_640][CLEAR_BUTTON].button },
385                 { "Reset",              1518,   159, 392,       UI_XSTR_COLOR_GREEN, -1, &Buttons[GR_640][RESET_BUTTON].button },
386                 { "Help",               1519,   500, 440,       UI_XSTR_COLOR_GREEN, -1, &Buttons[GR_640][HELP_BUTTON].button },
387                 { "Options",    1520,   479, 464,       UI_XSTR_COLOR_GREEN, -1, &Buttons[GR_640][OPTIONS_BUTTON].button },
388                 { "Accept",             1521,   573, 413,       UI_XSTR_COLOR_GREEN, -1, &Buttons[GR_640][ACCEPT_BUTTON].button }
389         }, 
390         { 
391                 // GR_1024
392                 { "Cancel",             1516,   30, 629,                UI_XSTR_COLOR_GREEN, -1, &Buttons[GR_1024][CANCEL_BUTTON].button },
393                 { "Clear",              1517,   151, 629,       UI_XSTR_COLOR_GREEN, -1, &Buttons[GR_1024][CLEAR_BUTTON].button },
394                 { "Reset",              1518,   269, 629,       UI_XSTR_COLOR_GREEN, -1, &Buttons[GR_1024][RESET_BUTTON].button },
395                 { "Help",               1519,   800, 704,       UI_XSTR_COLOR_GREEN, -1, &Buttons[GR_1024][HELP_BUTTON].button },
396                 { "Options",    1520,   797, 743,       UI_XSTR_COLOR_GREEN, -1, &Buttons[GR_1024][OPTIONS_BUTTON].button },
397                 { "Accept",             1521,   902, 661,       UI_XSTR_COLOR_GREEN, -1, &Buttons[GR_1024][ACCEPT_BUTTON].button }      
398         }
399 };
400
401
402
403 static struct {
404         char *label;
405         int type;
406         int index;
407         int y;  // Y coordinate of line
408 } Hotkey_lines[MAX_LINES];
409
410 static int Cur_hotkey = 0;
411 static int Scroll_offset;
412 static int Num_lines;
413 static int Selected_line;
414 static int Background_bitmap;
415 static int Wing_bmp;
416 static UI_WINDOW Ui_window;
417 static UI_BUTTON List_buttons[LIST_BUTTONS_MAX];  // buttons for each line of text in list
418 //static UI_BUTTON List_region;
419
420 //////////////////////
421
422
423 // function used in a couple of places to get the actual hotkey set number from a key value.
424 // not trivial since our current keysets (F5 - F12) do not have sequential keycodes
425 int mission_hotkey_get_set_num( int k )
426 {
427         int i;
428
429         for (i = 0; i < MAX_KEYED_TARGETS; i++ ) {
430                 if ( Key_sets[i] == k ) {
431                         return i;
432                 }
433         }
434
435         Int3();         // get allender
436         return 0;
437 }
438                 
439 // function to maybe restore some hotkeys during the first N seconds of the mission
440 void mission_hotkey_maybe_restore()
441 {
442         int i, index;
443
444         for ( i = 0; i < Num_hotkeys_saved; i++ ) {
445                 // don't process something that has no set
446                 if ( Hotkey_saved_info[i].setnum == -1 )
447                         continue;
448
449                 // the ship is present, add it to the given set.
450                 index = ship_name_lookup(Hotkey_saved_info[i].name);
451                 if ( index != -1 ) {
452                         hud_target_hotkey_add_remove( Hotkey_saved_info[i].setnum, &Objects[Ships[index].objnum], HOTKEY_USER_ADDED );
453                         Hotkey_saved_info[i].setnum = -1;
454                 }
455         }
456 }
457
458 // ---------------------------------------------------------------------
459 // mission_hotkey_set_defaults()
460 //
461 // Set up the hotkey lists for the player based on the mission designer
462 // defaults.  
463 //
464 void mission_hotkey_set_defaults()
465 {
466         int             i,j;
467         wing            *wp;
468         ship            *sp;
469         object  *A;
470
471         for ( i = 0; i < MAX_KEYED_TARGETS; i++ ) {
472                 hud_target_hotkey_clear(i);
473         }
474
475         // set the variable letting us know that we should save the hotkey sets
476         Hotkey_sets_saved = 0;
477         Mission_hotkey_save_timestamp = timestamp(HOTKEY_SAVE_TIME);
478
479         // if we have hotkeys saved from the previous run of this mission, then simply keep the cleared
480         // sets, and let the restore code take care of it!  This works because this function is currently
481         // only called from one place -- after the mission loads.
482         if ( Num_hotkeys_saved > 0 ) {
483                 mission_hotkey_maybe_restore();
484                 return;
485         }
486
487         // Check for ships with a hotkey assigned
488         obj_merge_created_list();
489         for ( A = GET_FIRST(&obj_used_list); A !=END_OF_LIST(&obj_used_list); A = GET_NEXT(A) ) {
490
491                 if ( (A == &obj_used_list) || (A->type != OBJ_SHIP) || ((Game_mode & GM_NORMAL) && (A == Player_obj)) ) {
492                         continue;
493                 }
494
495                 Assert(A->instance >= 0 && A->instance < MAX_SHIPS);
496                 sp = &Ships[A->instance];               
497
498                 if ( sp->hotkey == -1 )
499                         continue;
500
501                 // if the hotkey is the last hotkey in the list, then don't add it either since this hotkey is a special
502                 // marker to indicate that this ship should remain invisible in the hotkey screen until after mission
503                 // starts
504                 if ( sp->hotkey == MAX_KEYED_TARGETS )
505                         continue;
506
507                 Assert(sp->objnum >= 0);
508                 hud_target_hotkey_add_remove( sp->hotkey, &Objects[sp->objnum], HOTKEY_MISSION_FILE_ADDED );
509         }
510
511         // Check for wings with a hotkey assigned
512         for ( i = 0; i < num_wings; i++ ) {
513                 wp = &Wings[i];
514
515                 if ( wp->hotkey == -1 )  
516                         continue;
517
518                 // like ships, skip this wing if the hotkey is the last hotkey item
519                 if ( wp->hotkey == MAX_KEYED_TARGETS )
520                         continue;
521
522                 for ( j = 0; j < wp->current_count; j++ ) {
523                         if ( wp->ship_index[j] == -1 )
524                                 continue;
525
526                         sp = &Ships[wp->ship_index[j]];
527                         hud_target_hotkey_add_remove( wp->hotkey, &Objects[sp->objnum], HOTKEY_MISSION_FILE_ADDED );
528                 }                               
529         }
530 }
531
532 // function to reset the saved hotkeys -- called when a new mission is loaded
533 void mission_hotkey_reset_saved()
534 {
535         Num_hotkeys_saved = 0;
536 }
537
538 // next function called when we might want to save the hotkey sets for the player.  We will save the hotkey
539 // sets N seconds into the mission
540 void mission_hotkey_maybe_save_sets()
541 {
542         int i;
543         htarget_list    *hitem, *plist;
544         HK_save_info *hkp;
545
546         if ( !timestamp_elapsed(Mission_hotkey_save_timestamp) ) {
547                 mission_hotkey_maybe_restore();
548                 return;
549         }
550
551         // no processing if we have saved them.
552         if ( Hotkey_sets_saved )
553                 return;
554
555         for ( i = 0; i < MAX_HOTKEY_TARGET_ITEMS; i++ )
556                 Hotkey_saved_info[i].setnum = -1;
557
558         Num_hotkeys_saved = 0;
559         hkp = &(Hotkey_saved_info[0]);
560
561         for ( i = 0; i < MAX_KEYED_TARGETS; i++ ) {
562
563                 // get the list.  do nothing if list is empty
564                 plist = &(Player->keyed_targets[i]);
565                 if ( EMPTY(plist) )
566                         continue;
567
568                 for ( hitem = GET_FIRST(plist); hitem != END_OF_LIST(plist); hitem = GET_NEXT(hitem) ) {
569                         Assert( Num_hotkeys_saved < MAX_HOTKEY_TARGET_ITEMS );
570                         hkp->setnum = i;
571                         strcpy( hkp->name, Ships[hitem->objp->instance].ship_name );
572                         hkp++;
573                         Num_hotkeys_saved++;
574                 }
575         }
576
577         Hotkey_sets_saved = 1;
578 }
579
580 // function which gets called from MissionParse to maybe add a ship or wing to a hotkey set.
581 // this intermediate function is needed so that we don't blast over possibly saved hotkey sets
582 void mission_hotkey_mf_add( int set, int objnum, int how_to_add )
583 {
584         // if we are restoring hotkeys, and the timer hasn't elapsed, then return and let the
585         // hotkey restoration code deal with it
586         if ( Num_hotkeys_saved && !timestamp_elapsed(Mission_hotkey_save_timestamp) )
587                 return;
588
589         // we can add it to the set
590         hud_target_hotkey_add_remove( set, &Objects[objnum], how_to_add );
591 }
592
593 void mission_hotkey_validate()
594 {
595         htarget_list    *hitem, *plist;
596         object                  *A;
597         int                             obj_valid, i;
598
599         for ( i = 0; i < MAX_KEYED_TARGETS; i++ ) {
600                 plist = &(Players[Player_num].keyed_targets[i]);
601                 if ( EMPTY( plist ) )                   // no items in list, then do nothing
602                         continue;
603
604                 hitem = GET_FIRST(plist);
605                 while ( hitem != END_OF_LIST(plist) ) {
606
607                         // ensure this object is still valid and in the obj_used_list
608                         obj_valid = FALSE;
609                         for ( A = GET_FIRST(&obj_used_list); A !=END_OF_LIST(&obj_used_list); A = GET_NEXT(A) ) {
610                                 if ( A->signature == hitem->objp->signature ) {
611                                         obj_valid = TRUE;
612                                         break;
613                                 }
614                         }
615                         if ( obj_valid == FALSE ) {
616                                 htarget_list *temp;
617
618                                 temp = GET_NEXT(hitem);
619                                 list_remove( plist, hitem );
620                                 list_append( &htarget_free_list, hitem );
621                                 hitem->objp = NULL;
622                                 hitem = temp;
623                                 continue;
624                         }
625                         hitem = GET_NEXT( hitem );
626                 }       // end while
627         } // end for
628 }
629
630
631 // get the Hotkey_bits of a whole wing (bits must be set in all ships of wing for a hotkey bit to be set)
632 int get_wing_hotkeys(int n)
633 {
634         int i, total = 0xffffffff;
635
636         Assert((n >= 0) && (n < num_wings));
637         for (i=0; i<Wings[n].current_count; i++) {
638                 int ship_index;
639
640                 // don't count the player ship for the total -- you cannot assign the player since bad things
641                 // can happen on the hud.
642                 ship_index = Wings[n].ship_index[i];
643                 if ( &Ships[ship_index] == Player_ship )
644                         continue;
645
646                 total &= Hotkey_bits[Wings[n].ship_index[i]];
647         }
648
649         return total;
650 }
651
652 // add a line of hotkey smuck to end of list
653 int hotkey_line_add(char *text, int type, int index, int y)
654 {
655         if (Num_lines >= MAX_LINES)
656                 return 0;
657
658         Hotkey_lines[Num_lines].label = text;
659         Hotkey_lines[Num_lines].type = type;
660         Hotkey_lines[Num_lines].index = index;
661         Hotkey_lines[Num_lines].y = y;
662         return Num_lines++;
663 }
664
665 // insert a line of hotkey smuck before line 'n'.
666 int hotkey_line_insert(int n, char *text, int type, int index)
667 {
668         int z;
669
670         if (Num_lines >= MAX_LINES)
671                 return 0;
672
673         z = Num_lines++;
674         while (z > n) {
675                 Hotkey_lines[z] = Hotkey_lines[z - 1];
676                 z--;
677         }
678
679         Hotkey_lines[z].label = text;
680         Hotkey_lines[z].type = type;
681         Hotkey_lines[z].index = index;
682         return z;
683 }
684
685 // insert a line of hotkey smuck somewhere between 'start' and end of list such that it is
686 // sorted by name
687 int hotkey_line_add_sorted(char *text, int type, int index, int start)
688 {
689         int z;
690
691         if (Num_lines >= MAX_LINES)
692                 return -1;
693
694         z = Num_lines - 1;
695         while ((z >= start) && ((Hotkey_lines[z].type == HOTKEY_LINE_SUBSHIP) || (stricmp(text, Hotkey_lines[z].label) < 0)))
696                 z--;
697
698         z++;
699         while ((z < Num_lines) && (Hotkey_lines[z].type == HOTKEY_LINE_SUBSHIP))
700                 z++;
701
702         return hotkey_line_insert(z, text, type, index);
703 }
704
705 int hotkey_get_team(int i)
706 {
707         if (Ships[i].team == Player_ship->team)
708                 return TEAM_FRIENDLY;
709
710         return TEAM_HOSTILE;
711 }
712
713 int hotkey_build_team_listing(int team, int y)
714 {
715         ship_obj *so;
716         char *str = NULL;
717         int i, j, s, z, start;
718         int font_height = gr_get_font_height();
719
720         for (i=0; i<MAX_SHIPS; i++)
721                 if (hotkey_get_team(i) == team)
722                         break;
723
724         if (i >= MAX_SHIPS)
725                 return y;
726
727         if (team == Player_ship->team)
728                 str = XSTR( "Friendly ships", 402);
729         else {
730                 str = XSTR( "Enemy ships", 403);
731         }
732
733         hotkey_line_add(str, HOTKEY_LINE_HEADING, 0, y);
734         y += 2;
735
736         start = Num_lines;
737
738         // next loop used to loop through max ships, comparing team values.  MWA changed this to iterate
739         // through object list.  Seemed safer since it doesn't rely on the team value getting reset to
740         // a bogus value between missions
741         //for (i=0; i<MAX_SHIPS; i++) {
742         //      if ((Ships[i].team == team) && (Ships[i].wingnum < 0)) {
743         //              hotkey_line_add_sorted(Ships[i].ship_name, HOTKEY_LINE_SHIP, i, start);
744         //      }
745
746         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
747                 int shipnum;
748
749                 // don't process non-ships, or the player ship
750                 if ( (Game_mode & GM_NORMAL) && (so->objnum == OBJ_INDEX(Player_obj)) )
751                         continue;
752
753                 shipnum = Objects[so->objnum].instance;
754
755                 // filter out cargo containers, navbouys, etc
756                 if ( (Ship_info[Ships[shipnum].ship_info_index].flags & SIF_HARMLESS) && !(Ship_info[Ships[shipnum].ship_info_index].flags & SIF_ESCAPEPOD) )
757                         continue;
758
759                 // don't process non-ships (dunno what that would be, though).
760                 if (Ship_info[Ships[shipnum].ship_info_index].flags & SIF_NO_SHIP_TYPE)
761                         continue;
762
763                 // don't process ships invisible to sensors, dying or departing
764                 if ( Ships[shipnum].flags & (SF_HIDDEN_FROM_SENSORS|SF_DYING|SF_DEPARTING) )
765                         continue;
766
767                 // if a ship's hotkey is the last hotkey on the list, then maybe make the hotkey -1 if
768                 // we are now in mission.  Otherwise, skip this ship
769                 if ( Ships[shipnum].hotkey == MAX_KEYED_TARGETS ) {
770                         if ( !(Game_mode & GM_IN_MISSION) )
771                                 continue;                                                                               // skip to next ship
772                         Ships[shipnum].hotkey = -1;
773                 }
774
775                 // be sure this ship isn't in a wing, and that the teams match
776                 if ( (hotkey_get_team(shipnum) == team) && (Ships[shipnum].wingnum < 0) ) {
777                         hotkey_line_add_sorted(Ships[shipnum].ship_name, HOTKEY_LINE_SHIP, shipnum, start);
778                 }
779         }
780
781         for (i=0; i<num_wings; i++) {
782                 if (Wings[i].current_count && (hotkey_get_team( Wings[i].ship_index[Wings[i].special_ship] ) == team)) {
783
784                         // special check for the player's wing.  If he's in a wing, and the only guy left, don't
785                         // do anything
786                         if ( (Player_ship->wingnum == i) && (Wings[i].current_count == 1) )
787                                 continue;
788
789                         // if a ship's hotkey is the last hotkey on the list, then maybe make the hotkey -1 if
790                         // we are now in mission.  Otherwise, skip this ship
791                         if ( Wings[i].hotkey == MAX_KEYED_TARGETS ) {
792                                 if ( !(Game_mode & GM_IN_MISSION) )
793                                         continue;                                                                               // skip to next ship
794                                 Wings[i].hotkey = -1;
795                         }
796
797                         // don't add any wing data whose ships are hidden from sensors
798                         for ( j = 0; j < Wings[i].current_count; j++ ) {
799                                 if ( Ships[Wings[i].ship_index[j]].flags & SF_HIDDEN_FROM_SENSORS )
800                                         break;
801                         }
802                         // if we didn't reach the end of the list, don't display the wing
803                         if ( j < Wings[i].current_count )
804                                 continue;
805
806                         z = hotkey_line_add_sorted(Wings[i].name, HOTKEY_LINE_WING, i, start);
807                         if (Wings[i].flags & WF_EXPANDED) {
808                                 for (j=0; j<Wings[i].current_count; j++) {
809                                         s = Wings[i].ship_index[j];
810                                         z = hotkey_line_insert(z + 1, Ships[s].ship_name, HOTKEY_LINE_SUBSHIP, s);
811                                 }
812                         }
813                 }
814         }
815
816         z = HOTKEY_LINE_HEADING;
817         for (i=start; i<Num_lines; i++) {
818                 if (Hotkey_lines[i].type == HOTKEY_LINE_SUBSHIP)
819                         y += font_height;
820                 else
821                         y += font_height + 2;
822
823                 Hotkey_lines[i].y = y;
824         }
825
826         y += font_height + 8;
827         return y;
828 }
829
830 void hotkey_build_listing()
831 {
832         int y;
833
834         Num_lines = y = 0;
835
836         y = hotkey_build_team_listing(TEAM_FRIENDLY, y);
837         y = hotkey_build_team_listing(TEAM_HOSTILE, y);
838 }
839
840 int hotkey_line_query_visible(int n)
841 {
842         int y;
843
844         if ((n < 0) || (n >= Num_lines))
845                 return 0;
846         
847         y = Hotkey_lines[n].y - Hotkey_lines[Scroll_offset].y;
848         if ((y < 0) || (y + gr_get_font_height() > Hotkey_list_coords[gr_screen.res][3]))
849                 return 0;
850
851         return 1;
852 }
853
854 void hotkey_scroll_screen_up()
855 {
856         if (Scroll_offset) {
857                 Scroll_offset--;
858                 Assert(Selected_line > Scroll_offset);
859                 while (!hotkey_line_query_visible(Selected_line) || (Hotkey_lines[Selected_line].type == HOTKEY_LINE_HEADING))
860                         Selected_line--;
861
862                 gamesnd_play_iface(SND_SCROLL);
863
864         } else
865                 gamesnd_play_iface(SND_GENERAL_FAIL);
866 }
867
868 void hotkey_scroll_line_up()
869 {
870         if (Selected_line > 1) {
871                 Selected_line--;
872                 while (Hotkey_lines[Selected_line].type == HOTKEY_LINE_HEADING)
873                         Selected_line--;
874
875                 if (Selected_line < Scroll_offset)
876                         Scroll_offset = Selected_line;
877
878                 gamesnd_play_iface(SND_SCROLL);
879
880         } else
881                 gamesnd_play_iface(SND_GENERAL_FAIL);
882 }
883
884 void hotkey_scroll_screen_down()
885 {
886         if (Hotkey_lines[Num_lines - 1].y + gr_get_font_height() > Hotkey_lines[Scroll_offset].y + Hotkey_list_coords[gr_screen.res][3]) {
887                 Scroll_offset++;
888                 while (!hotkey_line_query_visible(Selected_line) || (Hotkey_lines[Selected_line].type == HOTKEY_LINE_HEADING)) {
889                         Selected_line++;
890                         Assert(Selected_line < Num_lines);
891                 }
892
893                 gamesnd_play_iface(SND_SCROLL);
894
895         } else
896                 gamesnd_play_iface(SND_GENERAL_FAIL);
897 }
898
899 void hotkey_scroll_line_down()
900 {
901         if (Selected_line < Num_lines - 1) {
902                 Selected_line++;
903                 while (Hotkey_lines[Selected_line].type == HOTKEY_LINE_HEADING)
904                         Selected_line++;
905
906                 Assert(Selected_line > Scroll_offset);
907                 while (!hotkey_line_query_visible(Selected_line))
908                         Scroll_offset++;
909
910                 gamesnd_play_iface(SND_SCROLL);
911
912         } else
913                 gamesnd_play_iface(SND_GENERAL_FAIL);
914 }
915
916 void expand_wing()
917 {
918         int i, z;
919
920         if (Hotkey_lines[Selected_line].type == HOTKEY_LINE_WING) {
921                 i = Hotkey_lines[Selected_line].index;
922                 Wings[i].flags ^= WF_EXPANDED;
923                 hotkey_build_listing();
924                 for (z=0; z<Num_lines; z++)
925                         if ((Hotkey_lines[z].type == HOTKEY_LINE_WING) && (Hotkey_lines[z].index == i)) {
926                                 Selected_line = z;
927                                 break;
928                         }
929         }
930 }
931
932 void reset_hotkeys()
933 {
934         int i;
935         htarget_list *hitem, *plist;
936
937         for (i=0; i<MAX_SHIPS; i++)
938                 Hotkey_bits[i] = 0;
939
940         for ( i=0; i<MAX_KEYED_TARGETS; i++ ) {
941                 plist = &(Players[Player_num].keyed_targets[i]);
942                 if ( EMPTY(plist) ) // no items in list, then do nothing
943                         continue;
944
945                 for ( hitem = GET_FIRST(plist); hitem != END_OF_LIST(plist); hitem = GET_NEXT(hitem) ) {
946                         Assert(hitem->objp->type == OBJ_SHIP);
947                         Hotkey_bits[hitem->objp->instance] |= (1 << i);
948                 }
949         }
950 }
951
952 void clear_hotkeys()
953 {
954         int i, b, z;
955
956         z = Hotkey_lines[Selected_line].type;
957         if (z == HOTKEY_LINE_WING) {
958                 z = Hotkey_lines[Selected_line].index;
959                 b = ~get_wing_hotkeys(z);
960                 for (i=0; i<Wings[z].current_count; i++)
961                         Hotkey_bits[Wings[z].ship_index[i]] &= b;
962
963         } else if ((z == HOTKEY_LINE_SHIP) || (z == HOTKEY_LINE_SUBSHIP)) {
964                 Hotkey_bits[Hotkey_lines[Selected_line].index] = 0;
965         }
966 }
967
968 void save_hotkeys()
969 {
970         ship_obj *so;
971         int i;
972
973         for (i=0; i<MAX_KEYED_TARGETS; i++) {
974                 hud_target_hotkey_clear(i);
975                 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
976                         if ( Hotkey_bits[Objects[so->objnum].instance] & (1 << i) ) {
977                                 hud_target_hotkey_add_remove(i, &Objects[so->objnum], HOTKEY_USER_ADDED );
978                         }
979                 }
980         }
981 }
982
983 void add_hotkey(int hotkey)
984 {
985         int i, z;
986
987         z = Hotkey_lines[Selected_line].type;
988         if (z == HOTKEY_LINE_WING) {
989                 z = Hotkey_lines[Selected_line].index;
990                 for (i=0; i<Wings[z].current_count; i++)
991                         Hotkey_bits[Wings[z].ship_index[i]] |= (1 << hotkey);
992
993         } else if ((z == HOTKEY_LINE_SHIP) || (z == HOTKEY_LINE_SUBSHIP)) {
994                 Hotkey_bits[Hotkey_lines[Selected_line].index] |= (1 << hotkey);
995         }
996 }
997
998 void remove_hotkey()
999 {
1000         int i, z;
1001
1002         z = Hotkey_lines[Selected_line].type;
1003         if (z == HOTKEY_LINE_WING) {
1004                 z = Hotkey_lines[Selected_line].index;
1005                 for (i=0; i<Wings[z].current_count; i++)
1006                         Hotkey_bits[Wings[z].ship_index[i]] &= ~(1 << Cur_hotkey);
1007
1008         } else if ((z == HOTKEY_LINE_SHIP) || (z == HOTKEY_LINE_SUBSHIP)) {
1009                 Hotkey_bits[Hotkey_lines[Selected_line].index] &= ~(1 << Cur_hotkey);
1010         }
1011 }
1012
1013 void hotkey_button_pressed(int n)
1014 {
1015         switch (n) {
1016                 case SCROLL_UP_BUTTON:
1017                         hotkey_scroll_screen_up();
1018                         break;
1019
1020                 case SCROLL_DOWN_BUTTON:
1021                         hotkey_scroll_screen_down();
1022                         break;
1023
1024                 case ADD_HOTKEY_BUTTON:
1025                         add_hotkey(Cur_hotkey);
1026                         gamesnd_play_iface(SND_USER_SELECT);
1027                         break;
1028
1029                 case REMOVE_HOTKEY_BUTTON:
1030                         remove_hotkey();
1031                         gamesnd_play_iface(SND_USER_SELECT);
1032                         break;
1033
1034                 case ACCEPT_BUTTON:
1035                         save_hotkeys();
1036                         // fall through to CANCEL_BUTTON
1037
1038                 case CANCEL_BUTTON:                     
1039                         mission_hotkey_exit();
1040                         gamesnd_play_iface(SND_USER_SELECT);
1041                         break;
1042
1043                 case HELP_BUTTON:
1044                         launch_context_help();
1045                         gamesnd_play_iface(SND_HELP_PRESSED);
1046                         break;
1047
1048                 case OPTIONS_BUTTON:                    
1049                         gameseq_post_event(GS_EVENT_OPTIONS_MENU);
1050                         gamesnd_play_iface(SND_USER_SELECT);
1051                         break;
1052
1053                 case CLEAR_BUTTON:
1054                         clear_hotkeys();
1055                         gamesnd_play_iface(SND_USER_SELECT);
1056                         break;
1057
1058                 case RESET_BUTTON:
1059                         reset_hotkeys();
1060                         gamesnd_play_iface(SND_USER_SELECT);
1061                         break;
1062         }
1063 }
1064
1065 // ---------------------------------------------------------------------
1066 // mission_hotkey_init()
1067 //
1068 // Initialize the hotkey assignment screen system.  Called when GS_STATE_HOTKEY_SCREEN
1069 // is entered.
1070 //
1071 void mission_hotkey_init()
1072 {
1073         int i;
1074         hotkey_buttons *b;
1075
1076         // pause all beam weapon sounds
1077         beam_pause_sounds();
1078
1079         // pause all game music
1080         audiostream_pause_all();
1081
1082         reset_hotkeys();
1083         common_set_interface_palette();  // set the interface palette
1084         Ui_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0);
1085         Ui_window.set_mask_bmap(Hotkey_mask_fname[gr_screen.res]);
1086
1087         for (i=0; i<NUM_BUTTONS; i++) {
1088                 b = &Buttons[gr_screen.res][i];
1089
1090                 b->button.create(&Ui_window, "", b->x, b->y, 60, 30, i < 2 ? 1 : 0, 1);
1091                 // set up callback for when a mouse first goes over a button
1092                 b->button.set_highlight_action(common_play_highlight_sound);
1093                 b->button.set_bmaps(b->filename);
1094                 b->button.link_hotspot(b->hotspot);
1095         }
1096
1097         // add all xstr text
1098         for(i=0; i<HOTKEY_NUM_TEXT; i++) {
1099                 Ui_window.add_XSTR(&Hotkey_text[gr_screen.res][i]);
1100         }
1101
1102         for (i=0; i<LIST_BUTTONS_MAX; i++) {
1103                 List_buttons[i].create(&Ui_window, "", 0, 0, 60, 30, (i < 2), 1);
1104                 List_buttons[i].hide();
1105                 List_buttons[i].disable();
1106         }
1107
1108         // set up hotkeys for buttons so we draw the correct animation frame when a key is pressed
1109         Buttons[gr_screen.res][SCROLL_UP_BUTTON].button.set_hotkey(KEY_PAGEUP);
1110         Buttons[gr_screen.res][SCROLL_DOWN_BUTTON].button.set_hotkey(KEY_PAGEDOWN);
1111
1112         // ensure help overlay is off
1113         help_overlay_set_state(HOTKEY_OVERLAY,0);
1114
1115         // load in relevant bitmaps
1116         Background_bitmap = bm_load(Hotkey_background_fname[gr_screen.res]);
1117         if (Background_bitmap < 0) {
1118                 // bitmap didnt load -- this is bad
1119                 Int3();
1120         }
1121         Wing_bmp = bm_load("WingDesignator");
1122         if (Wing_bmp < 0) {
1123                 // bitmap didnt load -- this is bad
1124                 Int3();
1125         }
1126
1127         Scroll_offset = 0;
1128         Selected_line = 1;
1129         hotkey_build_listing();
1130 }
1131
1132 // ---------------------------------------------------------------------
1133 // mission_hotkey_close()
1134 //
1135 // Cleanup the hotkey assignment screen system.  Called when GS_STATE_HOTKEY_SCREEN
1136 // is left.
1137 //
1138 void mission_hotkey_close()
1139 {
1140         if (Background_bitmap)
1141                 bm_unload(Background_bitmap);
1142         if (Wing_bmp >= 0)
1143                 bm_unload(Wing_bmp);
1144
1145         // unload the overlay bitmap
1146 //      help_overlay_unload(HOTKEY_OVERLAY);
1147
1148         // unpause all beam weapon sounds
1149         beam_unpause_sounds();
1150
1151         // unpause all game music
1152         audiostream_unpause_all();
1153
1154         Ui_window.destroy();
1155         common_free_interface_palette();                // restore game palette
1156         game_flush();
1157 }
1158
1159 // ---------------------------------------------------------------------
1160 // mission_hotkey_do_frame()
1161 //
1162 // Called once per frame to process user input for the Hotkey Assignment Screen
1163 //
1164 void mission_hotkey_do_frame(float frametime)
1165 {
1166         char buf[256];
1167         int i, k, w, h, y, z, line, hotkeys;
1168         int font_height = gr_get_font_height();
1169         int select_tease_line = -1;  // line mouse is down on, but won't be selected until button released
1170         color circle_color;
1171
1172         if ( help_overlay_active(HOTKEY_OVERLAY) ) {
1173                 Buttons[gr_screen.res][HELP_BUTTON].button.reset_status();
1174                 Ui_window.set_ignore_gadgets(1);
1175         }
1176
1177         k = Ui_window.process() & ~KEY_DEBUGGED;
1178
1179         if ( (k > 0) || B1_JUST_RELEASED ) {
1180                 if ( help_overlay_active(HOTKEY_OVERLAY) ) {
1181                         help_overlay_set_state(HOTKEY_OVERLAY, 0);
1182                         Ui_window.set_ignore_gadgets(0);
1183                         k = 0;
1184                 }
1185         }
1186
1187         if ( !help_overlay_active(HOTKEY_OVERLAY) ) {
1188                 Ui_window.set_ignore_gadgets(0);
1189         }
1190
1191         switch (k) {
1192                 case KEY_DOWN:  // scroll list down
1193                         hotkey_scroll_line_down();
1194                         break;
1195
1196                 case KEY_UP:  // scroll list up
1197                         hotkey_scroll_line_up();
1198                         break;
1199
1200                 case KEY_PAGEDOWN:  // scroll list down
1201                         hotkey_scroll_screen_down();
1202                         break;
1203
1204                 case KEY_PAGEUP:  // scroll list up
1205                         hotkey_scroll_screen_up();
1206                         break;
1207
1208                 case KEY_CTRLED | KEY_ENTER:
1209                         save_hotkeys();
1210                         // fall through to next state -- allender changed this behavior since ESC should always cancel, no?
1211
1212                 case KEY_ESC:                   
1213                         mission_hotkey_exit();
1214                         break;
1215
1216                 case KEY_TAB:
1217                 case KEY_ENTER:
1218                 case KEY_PADENTER:
1219                         expand_wing();
1220                         break;
1221
1222                 case KEY_EQUAL:
1223                 case KEY_PADPLUS:
1224                         add_hotkey(Cur_hotkey);
1225                         break;
1226
1227                 case KEY_MINUS:
1228                 case KEY_PADMINUS:
1229                         remove_hotkey();
1230                         break;
1231
1232                 case KEY_F2:                    
1233                         gameseq_post_event(GS_EVENT_OPTIONS_MENU);                      
1234                         break;
1235
1236                 case KEY_CTRLED | KEY_R:
1237                         reset_hotkeys();
1238                         break;
1239
1240                 case KEY_CTRLED | KEY_C:
1241                         clear_hotkeys();
1242                         break;
1243         }       // end switch
1244
1245         // ?
1246         for (i=0; i<MAX_KEYED_TARGETS; i++) {
1247                 if (k == Key_sets[i])
1248                         Cur_hotkey = i;
1249
1250                 if (k == (Key_sets[i] | KEY_SHIFTED))
1251                         add_hotkey(i);
1252         }
1253
1254         // handle pressed buttons
1255         for (i=0; i<NUM_BUTTONS; i++) {
1256                 if (Buttons[gr_screen.res][i].button.pressed()) {
1257                         hotkey_button_pressed(i);
1258                         break;                                  // only need to handle 1 button @ a time
1259                 }
1260         }
1261
1262         for (i=0; i<LIST_BUTTONS_MAX; i++) {
1263                 // check for tease line
1264                 if (List_buttons[i].button_down()) {
1265                         select_tease_line = i + Scroll_offset;
1266                 }
1267         
1268                 // check for selected list item
1269                 if (List_buttons[i].pressed()) {
1270                         Selected_line = i + Scroll_offset;
1271                         List_buttons[i].get_mouse_pos(&z, NULL);
1272                         z += Hotkey_list_coords[gr_screen.res][0];              // adjust to full screen space
1273                         if ((z >= Hotkey_wing_icon_x[gr_screen.res]) && (z < (Hotkey_wing_icon_x[gr_screen.res]) + Hotkey_function_field_width[gr_screen.res])) {
1274                                 expand_wing();
1275                         }
1276                 }
1277
1278                 if (List_buttons[i].double_clicked()) {
1279                         Selected_line = i + Scroll_offset;
1280                         hotkeys = -1;
1281                         switch (Hotkey_lines[Selected_line].type) {
1282                                 case HOTKEY_LINE_WING:
1283                                         hotkeys = get_wing_hotkeys(Hotkey_lines[Selected_line].index);
1284                                         break;
1285
1286                                 case HOTKEY_LINE_SHIP:
1287                                 case HOTKEY_LINE_SUBSHIP:
1288                                         hotkeys = Hotkey_bits[Hotkey_lines[Selected_line].index];
1289                                         break;
1290                         }
1291
1292                         if (hotkeys != -1) {
1293                                 if (hotkeys & (1 << Cur_hotkey))
1294                                         remove_hotkey();
1295                                 else
1296                                         add_hotkey(Cur_hotkey);
1297                         }
1298                 }
1299         }
1300
1301         if (Background_bitmap >= 0) {
1302                 gr_set_bitmap(Background_bitmap);
1303                 gr_bitmap(0, 0);
1304
1305         } else
1306                 gr_clear();
1307
1308         Ui_window.draw();
1309         gr_init_color(&circle_color, 160, 160, 0);
1310
1311         // draw the big "F10" in the little box 
1312         gr_set_font(FONT2);
1313         gr_set_color_fast(&Color_text_normal);
1314         strcpy(buf, Scan_code_text[Key_sets[Cur_hotkey]]);
1315         gr_get_string_size(&w, &h, buf);
1316         gr_printf(Hotkey_function_name_coords[gr_screen.res][0] + (Hotkey_function_name_coords[gr_screen.res][2] - w) / 2, Hotkey_function_name_coords[gr_screen.res][1], buf);
1317
1318         gr_set_font(FONT1);
1319         line = Scroll_offset;
1320         while (hotkey_line_query_visible(line)) {
1321                 z = Hotkey_lines[line].index;
1322                 y = Hotkey_list_coords[gr_screen.res][1] + Hotkey_lines[line].y - Hotkey_lines[Scroll_offset].y;
1323                 hotkeys = 0;
1324                 switch (Hotkey_lines[line].type) {
1325                         case HOTKEY_LINE_HEADING:
1326                                 gr_set_color_fast(&Color_text_heading);
1327
1328                                 gr_get_string_size(&w, &h, Hotkey_lines[line].label);
1329                                 i = y + h / 2 - 1;
1330                                 gr_line(Hotkey_list_coords[gr_screen.res][0], i, Hotkey_ship_x[gr_screen.res] - 2, i);
1331                                 gr_line(Hotkey_ship_x[gr_screen.res] + w + 1, i, Hotkey_list_coords[gr_screen.res][0] + Hotkey_list_coords[gr_screen.res][2], i);
1332                                 break;
1333
1334                         case HOTKEY_LINE_WING:
1335                                 gr_set_bitmap(Wing_bmp);
1336                                 bm_get_info(Wing_bmp, NULL, &h, NULL);
1337                                 i = y + font_height / 2 - h / 2 - 1;
1338                                 gr_bitmap(Hotkey_wing_icon_x[gr_screen.res], i);
1339
1340 //                              i = y + font_height / 2 - 1;
1341 //                              gr_set_color_fast(&circle_color);
1342 //                              gr_circle(ICON_LIST_X + 4, i, 5);
1343
1344 //                              gr_set_color_fast(&Color_bright);
1345 //                              gr_line(ICON_LIST_X, i, ICON_LIST_X + 2, i);
1346 //                              gr_line(ICON_LIST_X + 4, i - 4, ICON_LIST_X + 4, i - 2);
1347 //                              gr_line(ICON_LIST_X + 6, i, ICON_LIST_X + 8, i);
1348 //                              gr_line(ICON_LIST_X + 4, i + 2, ICON_LIST_X + 4, i + 4);
1349
1350                                 hotkeys = get_wing_hotkeys(Hotkey_lines[line].index);
1351                                 break;
1352
1353                         case HOTKEY_LINE_SHIP:
1354                         case HOTKEY_LINE_SUBSHIP:
1355                                 hotkeys = Hotkey_bits[Hotkey_lines[line].index];
1356                                 break;
1357
1358                         default:
1359                                 Int3();
1360                 }
1361
1362                 if (Hotkey_lines[line].type != HOTKEY_LINE_HEADING) {
1363                         List_buttons[line - Scroll_offset].update_dimensions(Hotkey_list_coords[gr_screen.res][0], y, Hotkey_list_coords[gr_screen.res][0] + Hotkey_list_coords[gr_screen.res][2] - Hotkey_list_coords[gr_screen.res][0], font_height);
1364                         List_buttons[line - Scroll_offset].enable();
1365                         if (hotkeys & (1 << Cur_hotkey)) {
1366                                 if (line == Selected_line)
1367                                         gr_set_color_fast(&Color_text_active_hi);
1368                                 else
1369                                         gr_set_color_fast(&Color_text_active);
1370
1371                         } else {
1372                                 if (line == Selected_line)
1373                                         gr_set_color_fast(&Color_text_selected);
1374                                 else if (line == select_tease_line)
1375                                         gr_set_color_fast(&Color_text_subselected);
1376                                 else
1377                                         gr_set_color_fast(&Color_text_normal);
1378                         }
1379
1380                 } else {
1381                         List_buttons[line - Scroll_offset].disable();
1382                 }
1383
1384                 // print active hotkeys associated for this line
1385                 if (hotkeys) {
1386                         for (i=0; i<MAX_KEYED_TARGETS; i++) {
1387                                 if (hotkeys & (1 << i)) {
1388                                         gr_printf(Hotkey_list_coords[gr_screen.res][0] + Hotkey_function_field_width[gr_screen.res]*i, y, Scan_code_text[Key_sets[i]]);
1389                                 }
1390                         }
1391 /*
1392                         *buf = 0;
1393                         for (i=0; i<MAX_KEYED_TARGETS; i++) {
1394                                 if (hotkeys & (1 << i)) {
1395                                         strcat(buf, Scan_code_text[Key_sets[i]]);
1396                                         strcat(buf, ", ");
1397                                 }
1398                         }
1399
1400                         Assert(strlen(buf) > 1);
1401                         buf[strlen(buf) - 2] = 0;  // lose the ", " on the end
1402
1403                         gr_force_fit_string(buf, 255, GROUP_LIST_W);
1404                         gr_printf(GROUP_LIST_X, y, buf);*/
1405                 }
1406         
1407                 // draw ship/wing name
1408                 strcpy(buf, Hotkey_lines[line].label);
1409                 if (Hotkey_lines[line].type == HOTKEY_LINE_SUBSHIP) {
1410                         // indent
1411                         gr_force_fit_string(buf, 255, Hotkey_list_coords[gr_screen.res][0] + Hotkey_list_coords[gr_screen.res][2] - (Hotkey_ship_x[gr_screen.res]+20));
1412                         gr_printf(Hotkey_ship_x[gr_screen.res]+20, y, buf);
1413                 } else {
1414                         gr_force_fit_string(buf, 255, Hotkey_list_coords[gr_screen.res][0] + Hotkey_list_coords[gr_screen.res][2] - Hotkey_ship_x[gr_screen.res]);
1415                         gr_printf(Hotkey_ship_x[gr_screen.res], y, buf);
1416                 }
1417
1418                 line++;
1419         }
1420
1421         i = line - Scroll_offset;
1422         while (i < LIST_BUTTONS_MAX)
1423                 List_buttons[i++].disable();
1424
1425         // blit help overlay if active
1426         help_overlay_maybe_blit(HOTKEY_OVERLAY);
1427
1428         gr_flip();
1429 }
1430
1431 void mission_hotkey_exit()
1432 {
1433         gameseq_post_event(GS_EVENT_PREVIOUS_STATE);
1434 }