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