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