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