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