]> icculus.org git repositories - taylor/freespace2.git/blob - src/mission/missionhotkey.cpp
try to avoid too much rumble
[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         SDLK_F5,
205         SDLK_F6,
206         SDLK_F7,
207         SDLK_F8,
208         SDLK_F9,
209         SDLK_F10,
210         SDLK_F11,
211         SDLK_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 const char *Hotkey_background_fname[GR_NUM_RESOLUTIONS] = {
233         "Hotkeys",              // GR_640
234         "2_Hotkeys"             // GR_1024
235 };
236
237 static const 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 struct hotkey_buttons {
374         const char *filename;
375         int x, y;
376         int hotspot;
377         UI_BUTTON button;  // because we have a class inside this struct, we need the constructor below..
378
379         hotkey_buttons(const char *name, int x1, int y1, int h) : filename(name), x(x1), y(y1), hotspot(h) {}
380 };
381
382 // button definitions
383 static hotkey_buttons Buttons[GR_NUM_RESOLUTIONS][NUM_BUTTONS] = {
384 //XSTR:OFF
385         {
386 #ifdef MAKE_FS1
387                 hotkey_buttons("HK_00", 0,              87,             0),             // scroll up
388                 hotkey_buttons("HK_01", 0,              131,    1),             // scroll down
389                 hotkey_buttons("HK_04", 216,    351,    4),             // cancel
390                 hotkey_buttons("HK_08", 286,    351,    8),             // clear
391                 hotkey_buttons("HK_09", 362,    351,    9),             // reset
392                 hotkey_buttons("HK_02", 591,    80,             2),             // add hotkey
393                 hotkey_buttons("HK_03", 591,    144,    3),             // remove hotkey
394                 hotkey_buttons("HK_06", 469,    424,    6),             // help
395                 hotkey_buttons("HK_07", 448,    452,    7),             // options
396                 hotkey_buttons("HK_05", 561,    411,    5)              // accept
397 #else
398                 // GR_640
399                 hotkey_buttons("HKB_00",        1,              94,     0),
400                 hotkey_buttons("HKB_01",        1,              133,    1),
401                 hotkey_buttons("HKB_02",        15,     342,    2),
402                 hotkey_buttons("HKB_03",        84,     342,    3),
403                 hotkey_buttons("HKB_04",        161,    342,    4),
404                 hotkey_buttons("HKB_05",        539,    5,              5),
405                 hotkey_buttons("HKB_06",        539,    44,     6),
406                 hotkey_buttons("HKB_07",        539,    431,    7),
407                 hotkey_buttons("HKB_08",        539,    455,    8),
408                 hotkey_buttons("HKB_09",        575,    432,    9)
409 #endif
410         },
411         {
412                 // GR_1024
413                 hotkey_buttons("2_HKB_00",              2,              150,    0),
414                 hotkey_buttons("2_HKB_01",              2,              213,    1),
415                 hotkey_buttons("2_HKB_02",              24,     548,    2),
416                 hotkey_buttons("2_HKB_03",              135,    548,    3),
417                 hotkey_buttons("2_HKB_04",              258,    548,    4),
418                 hotkey_buttons("2_HKB_05",              862,    8,              5),
419                 hotkey_buttons("2_HKB_06",              862,    71,     6),
420                 hotkey_buttons("2_HKB_07",              863,    690,    7),
421                 hotkey_buttons("2_HKB_08",              862,    728,    8),
422                 hotkey_buttons("2_HKB_09",              920,    692,    9)
423         }
424 //XSTR:ON
425 };
426
427 #ifndef MAKE_FS1
428 #define HOTKEY_NUM_TEXT         6
429
430 static UI_XSTR Hotkey_text[GR_NUM_RESOLUTIONS][HOTKEY_NUM_TEXT] = {
431         { 
432                 // GR_640
433                 { "Cancel",             1516,   7,      392,            UI_XSTR_COLOR_GREEN, -1, &Buttons[GR_640][CANCEL_BUTTON].button },
434                 { "Clear",              1517,   85, 392,                UI_XSTR_COLOR_GREEN, -1, &Buttons[GR_640][CLEAR_BUTTON].button },
435                 { "Reset",              1518,   159, 392,       UI_XSTR_COLOR_GREEN, -1, &Buttons[GR_640][RESET_BUTTON].button },
436                 { "Help",               1519,   500, 440,       UI_XSTR_COLOR_GREEN, -1, &Buttons[GR_640][HELP_BUTTON].button },
437                 { "Options",    1520,   479, 464,       UI_XSTR_COLOR_GREEN, -1, &Buttons[GR_640][OPTIONS_BUTTON].button },
438                 { "Accept",             1521,   573, 413,       UI_XSTR_COLOR_GREEN, -1, &Buttons[GR_640][ACCEPT_BUTTON].button }
439         }, 
440         { 
441                 // GR_1024
442                 { "Cancel",             1516,   30, 629,                UI_XSTR_COLOR_GREEN, -1, &Buttons[GR_1024][CANCEL_BUTTON].button },
443                 { "Clear",              1517,   151, 629,       UI_XSTR_COLOR_GREEN, -1, &Buttons[GR_1024][CLEAR_BUTTON].button },
444                 { "Reset",              1518,   269, 629,       UI_XSTR_COLOR_GREEN, -1, &Buttons[GR_1024][RESET_BUTTON].button },
445                 { "Help",               1519,   800, 704,       UI_XSTR_COLOR_GREEN, -1, &Buttons[GR_1024][HELP_BUTTON].button },
446                 { "Options",    1520,   797, 743,       UI_XSTR_COLOR_GREEN, -1, &Buttons[GR_1024][OPTIONS_BUTTON].button },
447                 { "Accept",             1521,   902, 661,       UI_XSTR_COLOR_GREEN, -1, &Buttons[GR_1024][ACCEPT_BUTTON].button }      
448         }
449 };
450 #endif
451
452
453
454 static struct {
455         const char *label;
456         int type;
457         int index;
458         int y;  // Y coordinate of line
459 } Hotkey_lines[MAX_LINES];
460
461 static int Cur_hotkey = 0;
462 static int Scroll_offset;
463 static int Num_lines;
464 static int Selected_line;
465 static int Background_bitmap;
466 static int Wing_bmp;
467 static UI_WINDOW Ui_window;
468 static UI_BUTTON List_buttons[LIST_BUTTONS_MAX];  // buttons for each line of text in list
469 //static UI_BUTTON List_region;
470
471 //////////////////////
472
473
474 // function used in a couple of places to get the actual hotkey set number from a key value.
475 // not trivial since our current keysets (F5 - F12) do not have sequential keycodes
476 int mission_hotkey_get_set_num( int k )
477 {
478         int i;
479
480         for (i = 0; i < MAX_KEYED_TARGETS; i++ ) {
481                 if ( Key_sets[i] == k ) {
482                         return i;
483                 }
484         }
485
486         Int3();         // get allender
487         return 0;
488 }
489                 
490 // function to maybe restore some hotkeys during the first N seconds of the mission
491 void mission_hotkey_maybe_restore()
492 {
493         int i, index;
494
495         for ( i = 0; i < Num_hotkeys_saved; i++ ) {
496                 // don't process something that has no set
497                 if ( Hotkey_saved_info[i].setnum == -1 )
498                         continue;
499
500                 // the ship is present, add it to the given set.
501                 index = ship_name_lookup(Hotkey_saved_info[i].name);
502                 if ( index != -1 ) {
503                         hud_target_hotkey_add_remove( Hotkey_saved_info[i].setnum, &Objects[Ships[index].objnum], HOTKEY_USER_ADDED );
504                         Hotkey_saved_info[i].setnum = -1;
505                 }
506         }
507 }
508
509 // ---------------------------------------------------------------------
510 // mission_hotkey_set_defaults()
511 //
512 // Set up the hotkey lists for the player based on the mission designer
513 // defaults.  
514 //
515 void mission_hotkey_set_defaults()
516 {
517         int             i,j;
518         wing            *wp;
519         ship            *sp;
520         object  *A;
521
522         for ( i = 0; i < MAX_KEYED_TARGETS; i++ ) {
523                 hud_target_hotkey_clear(i);
524         }
525
526         // set the variable letting us know that we should save the hotkey sets
527         Hotkey_sets_saved = 0;
528         Mission_hotkey_save_timestamp = timestamp(HOTKEY_SAVE_TIME);
529
530         // if we have hotkeys saved from the previous run of this mission, then simply keep the cleared
531         // sets, and let the restore code take care of it!  This works because this function is currently
532         // only called from one place -- after the mission loads.
533         if ( Num_hotkeys_saved > 0 ) {
534                 mission_hotkey_maybe_restore();
535                 return;
536         }
537
538         // Check for ships with a hotkey assigned
539         obj_merge_created_list();
540         for ( A = GET_FIRST(&obj_used_list); A !=END_OF_LIST(&obj_used_list); A = GET_NEXT(A) ) {
541
542                 if ( (A == &obj_used_list) || (A->type != OBJ_SHIP) || ((Game_mode & GM_NORMAL) && (A == Player_obj)) ) {
543                         continue;
544                 }
545
546                 SDL_assert(A->instance >= 0 && A->instance < MAX_SHIPS);
547                 sp = &Ships[A->instance];               
548
549                 if ( sp->hotkey == -1 )
550                         continue;
551
552                 // if the hotkey is the last hotkey in the list, then don't add it either since this hotkey is a special
553                 // marker to indicate that this ship should remain invisible in the hotkey screen until after mission
554                 // starts
555                 if ( sp->hotkey == MAX_KEYED_TARGETS )
556                         continue;
557
558                 SDL_assert(sp->objnum >= 0);
559                 hud_target_hotkey_add_remove( sp->hotkey, &Objects[sp->objnum], HOTKEY_MISSION_FILE_ADDED );
560         }
561
562         // Check for wings with a hotkey assigned
563         for ( i = 0; i < num_wings; i++ ) {
564                 wp = &Wings[i];
565
566                 if ( wp->hotkey == -1 )  
567                         continue;
568
569                 // like ships, skip this wing if the hotkey is the last hotkey item
570                 if ( wp->hotkey == MAX_KEYED_TARGETS )
571                         continue;
572
573                 for ( j = 0; j < wp->current_count; j++ ) {
574                         if ( wp->ship_index[j] == -1 )
575                                 continue;
576
577                         sp = &Ships[wp->ship_index[j]];
578                         hud_target_hotkey_add_remove( wp->hotkey, &Objects[sp->objnum], HOTKEY_MISSION_FILE_ADDED );
579                 }                               
580         }
581 }
582
583 // function to reset the saved hotkeys -- called when a new mission is loaded
584 void mission_hotkey_reset_saved()
585 {
586         Num_hotkeys_saved = 0;
587 }
588
589 // next function called when we might want to save the hotkey sets for the player.  We will save the hotkey
590 // sets N seconds into the mission
591 void mission_hotkey_maybe_save_sets()
592 {
593         int i;
594         htarget_list    *hitem, *plist;
595         HK_save_info *hkp;
596
597         if ( !timestamp_elapsed(Mission_hotkey_save_timestamp) ) {
598                 mission_hotkey_maybe_restore();
599                 return;
600         }
601
602         // no processing if we have saved them.
603         if ( Hotkey_sets_saved )
604                 return;
605
606         for ( i = 0; i < MAX_HOTKEY_TARGET_ITEMS; i++ )
607                 Hotkey_saved_info[i].setnum = -1;
608
609         Num_hotkeys_saved = 0;
610         hkp = &(Hotkey_saved_info[0]);
611
612         for ( i = 0; i < MAX_KEYED_TARGETS; i++ ) {
613
614                 // get the list.  do nothing if list is empty
615                 plist = &(Player->keyed_targets[i]);
616                 if ( EMPTY(plist) )
617                         continue;
618
619                 for ( hitem = GET_FIRST(plist); hitem != END_OF_LIST(plist); hitem = GET_NEXT(hitem) ) {
620                         SDL_assert( Num_hotkeys_saved < MAX_HOTKEY_TARGET_ITEMS );
621                         hkp->setnum = i;
622                         SDL_strlcpy( hkp->name, Ships[hitem->objp->instance].ship_name, SDL_arraysize(hkp->name) );
623                         hkp++;
624                         Num_hotkeys_saved++;
625                 }
626         }
627
628         Hotkey_sets_saved = 1;
629 }
630
631 // function which gets called from MissionParse to maybe add a ship or wing to a hotkey set.
632 // this intermediate function is needed so that we don't blast over possibly saved hotkey sets
633 void mission_hotkey_mf_add( int set, int objnum, int how_to_add )
634 {
635         // if we are restoring hotkeys, and the timer hasn't elapsed, then return and let the
636         // hotkey restoration code deal with it
637         if ( Num_hotkeys_saved && !timestamp_elapsed(Mission_hotkey_save_timestamp) )
638                 return;
639
640         // we can add it to the set
641         hud_target_hotkey_add_remove( set, &Objects[objnum], how_to_add );
642 }
643
644 void mission_hotkey_validate()
645 {
646         htarget_list    *hitem, *plist;
647         object                  *A;
648         int                             obj_valid, i;
649
650         for ( i = 0; i < MAX_KEYED_TARGETS; i++ ) {
651                 plist = &(Players[Player_num].keyed_targets[i]);
652                 if ( EMPTY( plist ) )                   // no items in list, then do nothing
653                         continue;
654
655                 hitem = GET_FIRST(plist);
656                 while ( hitem != END_OF_LIST(plist) ) {
657
658                         // ensure this object is still valid and in the obj_used_list
659                         obj_valid = FALSE;
660                         for ( A = GET_FIRST(&obj_used_list); A !=END_OF_LIST(&obj_used_list); A = GET_NEXT(A) ) {
661                                 if ( A->signature == hitem->objp->signature ) {
662                                         obj_valid = TRUE;
663                                         break;
664                                 }
665                         }
666                         if ( obj_valid == FALSE ) {
667                                 htarget_list *temp;
668
669                                 temp = GET_NEXT(hitem);
670                                 list_remove( plist, hitem );
671                                 list_append( &htarget_free_list, hitem );
672                                 hitem->objp = NULL;
673                                 hitem = temp;
674                                 continue;
675                         }
676                         hitem = GET_NEXT( hitem );
677                 }       // end while
678         } // end for
679 }
680
681
682 // get the Hotkey_bits of a whole wing (bits must be set in all ships of wing for a hotkey bit to be set)
683 int get_wing_hotkeys(int n)
684 {
685         int i, total = 0xffffffff;
686
687         SDL_assert((n >= 0) && (n < num_wings));
688         for (i=0; i<Wings[n].current_count; i++) {
689                 int ship_index;
690
691                 // don't count the player ship for the total -- you cannot assign the player since bad things
692                 // can happen on the hud.
693                 ship_index = Wings[n].ship_index[i];
694                 if ( &Ships[ship_index] == Player_ship )
695                         continue;
696
697                 total &= Hotkey_bits[Wings[n].ship_index[i]];
698         }
699
700         return total;
701 }
702
703 // add a line of hotkey smuck to end of list
704 int hotkey_line_add(const char *text, int type, int index, int y)
705 {
706         if (Num_lines >= MAX_LINES)
707                 return 0;
708
709         Hotkey_lines[Num_lines].label = text;
710         Hotkey_lines[Num_lines].type = type;
711         Hotkey_lines[Num_lines].index = index;
712         Hotkey_lines[Num_lines].y = y;
713         return Num_lines++;
714 }
715
716 // insert a line of hotkey smuck before line 'n'.
717 int hotkey_line_insert(int n, const char *text, int type, int index)
718 {
719         int z;
720
721         if (Num_lines >= MAX_LINES)
722                 return 0;
723
724         z = Num_lines++;
725         while (z > n) {
726                 Hotkey_lines[z] = Hotkey_lines[z - 1];
727                 z--;
728         }
729
730         Hotkey_lines[z].label = text;
731         Hotkey_lines[z].type = type;
732         Hotkey_lines[z].index = index;
733         return z;
734 }
735
736 // insert a line of hotkey smuck somewhere between 'start' and end of list such that it is
737 // sorted by name
738 int hotkey_line_add_sorted(const char *text, int type, int index, int start)
739 {
740         int z;
741
742         if (Num_lines >= MAX_LINES)
743                 return -1;
744
745         z = Num_lines - 1;
746         while ((z >= start) && ((Hotkey_lines[z].type == HOTKEY_LINE_SUBSHIP) || (SDL_strcasecmp(text, Hotkey_lines[z].label) < 0)))
747                 z--;
748
749         z++;
750         while ((z < Num_lines) && (Hotkey_lines[z].type == HOTKEY_LINE_SUBSHIP))
751                 z++;
752
753         return hotkey_line_insert(z, text, type, index);
754 }
755
756 int hotkey_get_team(int i)
757 {
758         if (Ships[i].team == Player_ship->team)
759                 return TEAM_FRIENDLY;
760
761         return TEAM_HOSTILE;
762 }
763
764 int hotkey_build_team_listing(int team, int y)
765 {
766         ship_obj *so;
767         const char *str = NULL;
768         int i, j, s, z, start;
769         int font_height = gr_get_font_height();
770
771         for (i=0; i<MAX_SHIPS; i++)
772                 if (hotkey_get_team(i) == team)
773                         break;
774
775         if (i >= MAX_SHIPS)
776                 return y;
777
778         if (team == Player_ship->team)
779                 str = XSTR( "Friendly ships", 402);
780         else {
781                 str = XSTR( "Enemy ships", 403);
782         }
783
784         hotkey_line_add(str, HOTKEY_LINE_HEADING, 0, y);
785         y += 2;
786
787         start = Num_lines;
788
789         // next loop used to loop through max ships, comparing team values.  MWA changed this to iterate
790         // through object list.  Seemed safer since it doesn't rely on the team value getting reset to
791         // a bogus value between missions
792         //for (i=0; i<MAX_SHIPS; i++) {
793         //      if ((Ships[i].team == team) && (Ships[i].wingnum < 0)) {
794         //              hotkey_line_add_sorted(Ships[i].ship_name, HOTKEY_LINE_SHIP, i, start);
795         //      }
796
797         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
798                 int shipnum;
799
800                 // don't process non-ships, or the player ship
801                 if ( (Game_mode & GM_NORMAL) && (so->objnum == OBJ_INDEX(Player_obj)) )
802                         continue;
803
804                 shipnum = Objects[so->objnum].instance;
805
806                 // filter out cargo containers, navbouys, etc
807                 if ( (Ship_info[Ships[shipnum].ship_info_index].flags & SIF_HARMLESS) && !(Ship_info[Ships[shipnum].ship_info_index].flags & SIF_ESCAPEPOD) )
808                         continue;
809
810                 // don't process non-ships (dunno what that would be, though).
811                 if (Ship_info[Ships[shipnum].ship_info_index].flags & SIF_NO_SHIP_TYPE)
812                         continue;
813
814                 // don't process ships invisible to sensors, dying or departing
815                 if ( Ships[shipnum].flags & (SF_HIDDEN_FROM_SENSORS|SF_DYING|SF_DEPARTING) )
816                         continue;
817
818                 // if a ship's hotkey is the last hotkey on the list, then maybe make the hotkey -1 if
819                 // we are now in mission.  Otherwise, skip this ship
820                 if ( Ships[shipnum].hotkey == MAX_KEYED_TARGETS ) {
821                         if ( !(Game_mode & GM_IN_MISSION) )
822                                 continue;                                                                               // skip to next ship
823                         Ships[shipnum].hotkey = -1;
824                 }
825
826                 // be sure this ship isn't in a wing, and that the teams match
827                 if ( (hotkey_get_team(shipnum) == team) && (Ships[shipnum].wingnum < 0) ) {
828                         hotkey_line_add_sorted(Ships[shipnum].ship_name, HOTKEY_LINE_SHIP, shipnum, start);
829                 }
830         }
831
832         for (i=0; i<num_wings; i++) {
833                 if (Wings[i].current_count && (hotkey_get_team( Wings[i].ship_index[Wings[i].special_ship] ) == team)) {
834
835                         // special check for the player's wing.  If he's in a wing, and the only guy left, don't
836                         // do anything
837                         if ( (Player_ship->wingnum == i) && (Wings[i].current_count == 1) )
838                                 continue;
839
840                         // if a ship's hotkey is the last hotkey on the list, then maybe make the hotkey -1 if
841                         // we are now in mission.  Otherwise, skip this ship
842                         if ( Wings[i].hotkey == MAX_KEYED_TARGETS ) {
843                                 if ( !(Game_mode & GM_IN_MISSION) )
844                                         continue;                                                                               // skip to next ship
845                                 Wings[i].hotkey = -1;
846                         }
847
848                         // don't add any wing data whose ships are hidden from sensors
849                         for ( j = 0; j < Wings[i].current_count; j++ ) {
850                                 if ( Ships[Wings[i].ship_index[j]].flags & SF_HIDDEN_FROM_SENSORS )
851                                         break;
852                         }
853                         // if we didn't reach the end of the list, don't display the wing
854                         if ( j < Wings[i].current_count )
855                                 continue;
856
857                         z = hotkey_line_add_sorted(Wings[i].name, HOTKEY_LINE_WING, i, start);
858                         if (Wings[i].flags & WF_EXPANDED) {
859                                 for (j=0; j<Wings[i].current_count; j++) {
860                                         s = Wings[i].ship_index[j];
861                                         z = hotkey_line_insert(z + 1, Ships[s].ship_name, HOTKEY_LINE_SUBSHIP, s);
862                                 }
863                         }
864                 }
865         }
866
867         //z = HOTKEY_LINE_HEADING;
868         for (i=start; i<Num_lines; i++) {
869                 if (Hotkey_lines[i].type == HOTKEY_LINE_SUBSHIP)
870                         y += font_height;
871                 else
872                         y += font_height + 2;
873
874                 Hotkey_lines[i].y = y;
875         }
876
877         y += font_height + 8;
878         return y;
879 }
880
881 void hotkey_build_listing()
882 {
883         int y;
884
885         Num_lines = y = 0;
886
887         y = hotkey_build_team_listing(TEAM_FRIENDLY, y);
888         hotkey_build_team_listing(TEAM_HOSTILE, y);
889 }
890
891 int hotkey_line_query_visible(int n)
892 {
893         int y;
894
895         if ((n < 0) || (n >= Num_lines))
896                 return 0;
897         
898         y = Hotkey_lines[n].y - Hotkey_lines[Scroll_offset].y;
899         if ((y < 0) || (y + gr_get_font_height() > Hotkey_list_coords[gr_screen.res][3]))
900                 return 0;
901
902         return 1;
903 }
904
905 void hotkey_scroll_screen_up()
906 {
907         if (Scroll_offset) {
908                 Scroll_offset--;
909                 SDL_assert(Selected_line > Scroll_offset);
910                 while (!hotkey_line_query_visible(Selected_line) || (Hotkey_lines[Selected_line].type == HOTKEY_LINE_HEADING))
911                         Selected_line--;
912
913                 gamesnd_play_iface(SND_SCROLL);
914
915         } else
916                 gamesnd_play_iface(SND_GENERAL_FAIL);
917 }
918
919 void hotkey_scroll_line_up()
920 {
921         if (Selected_line > 1) {
922                 Selected_line--;
923                 while (Hotkey_lines[Selected_line].type == HOTKEY_LINE_HEADING)
924                         Selected_line--;
925
926                 if (Selected_line < Scroll_offset)
927                         Scroll_offset = Selected_line;
928
929                 gamesnd_play_iface(SND_SCROLL);
930
931         } else
932                 gamesnd_play_iface(SND_GENERAL_FAIL);
933 }
934
935 void hotkey_scroll_screen_down()
936 {
937         if (Hotkey_lines[Num_lines - 1].y + gr_get_font_height() > Hotkey_lines[Scroll_offset].y + Hotkey_list_coords[gr_screen.res][3]) {
938                 Scroll_offset++;
939                 while (!hotkey_line_query_visible(Selected_line) || (Hotkey_lines[Selected_line].type == HOTKEY_LINE_HEADING)) {
940                         Selected_line++;
941                         SDL_assert(Selected_line < Num_lines);
942                 }
943
944                 gamesnd_play_iface(SND_SCROLL);
945
946         } else
947                 gamesnd_play_iface(SND_GENERAL_FAIL);
948 }
949
950 void hotkey_scroll_line_down()
951 {
952         if (Selected_line < Num_lines - 1) {
953                 Selected_line++;
954                 while (Hotkey_lines[Selected_line].type == HOTKEY_LINE_HEADING)
955                         Selected_line++;
956
957                 SDL_assert(Selected_line > Scroll_offset);
958                 while (!hotkey_line_query_visible(Selected_line))
959                         Scroll_offset++;
960
961                 gamesnd_play_iface(SND_SCROLL);
962
963         } else
964                 gamesnd_play_iface(SND_GENERAL_FAIL);
965 }
966
967 void expand_wing()
968 {
969         int i, z;
970
971         if (Hotkey_lines[Selected_line].type == HOTKEY_LINE_WING) {
972                 i = Hotkey_lines[Selected_line].index;
973                 Wings[i].flags ^= WF_EXPANDED;
974                 hotkey_build_listing();
975                 for (z=0; z<Num_lines; z++)
976                         if ((Hotkey_lines[z].type == HOTKEY_LINE_WING) && (Hotkey_lines[z].index == i)) {
977                                 Selected_line = z;
978                                 break;
979                         }
980         }
981 }
982
983 void reset_hotkeys()
984 {
985         int i;
986         htarget_list *hitem, *plist;
987
988         for (i=0; i<MAX_SHIPS; i++)
989                 Hotkey_bits[i] = 0;
990
991         for ( i=0; i<MAX_KEYED_TARGETS; i++ ) {
992                 plist = &(Players[Player_num].keyed_targets[i]);
993                 if ( EMPTY(plist) ) // no items in list, then do nothing
994                         continue;
995
996                 for ( hitem = GET_FIRST(plist); hitem != END_OF_LIST(plist); hitem = GET_NEXT(hitem) ) {
997                         SDL_assert(hitem->objp->type == OBJ_SHIP);
998                         Hotkey_bits[hitem->objp->instance] |= (1 << i);
999                 }
1000         }
1001 }
1002
1003 void clear_hotkeys()
1004 {
1005         int i, b, z;
1006
1007         z = Hotkey_lines[Selected_line].type;
1008         if (z == HOTKEY_LINE_WING) {
1009                 z = Hotkey_lines[Selected_line].index;
1010                 b = ~get_wing_hotkeys(z);
1011                 for (i=0; i<Wings[z].current_count; i++)
1012                         Hotkey_bits[Wings[z].ship_index[i]] &= b;
1013
1014         } else if ((z == HOTKEY_LINE_SHIP) || (z == HOTKEY_LINE_SUBSHIP)) {
1015                 Hotkey_bits[Hotkey_lines[Selected_line].index] = 0;
1016         }
1017 }
1018
1019 void save_hotkeys()
1020 {
1021         ship_obj *so;
1022         int i;
1023
1024         for (i=0; i<MAX_KEYED_TARGETS; i++) {
1025                 hud_target_hotkey_clear(i);
1026                 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
1027                         if ( Hotkey_bits[Objects[so->objnum].instance] & (1 << i) ) {
1028                                 hud_target_hotkey_add_remove(i, &Objects[so->objnum], HOTKEY_USER_ADDED );
1029                         }
1030                 }
1031         }
1032 }
1033
1034 void add_hotkey(int hotkey)
1035 {
1036         int i, z;
1037
1038         z = Hotkey_lines[Selected_line].type;
1039         if (z == HOTKEY_LINE_WING) {
1040                 z = Hotkey_lines[Selected_line].index;
1041                 for (i=0; i<Wings[z].current_count; i++)
1042                         Hotkey_bits[Wings[z].ship_index[i]] |= (1 << hotkey);
1043
1044         } else if ((z == HOTKEY_LINE_SHIP) || (z == HOTKEY_LINE_SUBSHIP)) {
1045                 Hotkey_bits[Hotkey_lines[Selected_line].index] |= (1 << hotkey);
1046         }
1047 }
1048
1049 void remove_hotkey()
1050 {
1051         int i, z;
1052
1053         z = Hotkey_lines[Selected_line].type;
1054         if (z == HOTKEY_LINE_WING) {
1055                 z = Hotkey_lines[Selected_line].index;
1056                 for (i=0; i<Wings[z].current_count; i++)
1057                         Hotkey_bits[Wings[z].ship_index[i]] &= ~(1 << Cur_hotkey);
1058
1059         } else if ((z == HOTKEY_LINE_SHIP) || (z == HOTKEY_LINE_SUBSHIP)) {
1060                 Hotkey_bits[Hotkey_lines[Selected_line].index] &= ~(1 << Cur_hotkey);
1061         }
1062 }
1063
1064 void hotkey_button_pressed(int n)
1065 {
1066         switch (n) {
1067                 case SCROLL_UP_BUTTON:
1068                         hotkey_scroll_screen_up();
1069                         break;
1070
1071                 case SCROLL_DOWN_BUTTON:
1072                         hotkey_scroll_screen_down();
1073                         break;
1074
1075                 case ADD_HOTKEY_BUTTON:
1076                         add_hotkey(Cur_hotkey);
1077                         gamesnd_play_iface(SND_USER_SELECT);
1078                         break;
1079
1080                 case REMOVE_HOTKEY_BUTTON:
1081                         remove_hotkey();
1082                         gamesnd_play_iface(SND_USER_SELECT);
1083                         break;
1084
1085                 case ACCEPT_BUTTON:
1086                         save_hotkeys();
1087                         // fall through to CANCEL_BUTTON
1088
1089                 case CANCEL_BUTTON:                     
1090                         mission_hotkey_exit();
1091                         gamesnd_play_iface(SND_USER_SELECT);
1092                         break;
1093
1094                 case HELP_BUTTON:
1095                         launch_context_help();
1096                         gamesnd_play_iface(SND_HELP_PRESSED);
1097                         break;
1098
1099                 case OPTIONS_BUTTON:                    
1100                         gameseq_post_event(GS_EVENT_OPTIONS_MENU);
1101                         gamesnd_play_iface(SND_USER_SELECT);
1102                         break;
1103
1104                 case CLEAR_BUTTON:
1105                         clear_hotkeys();
1106                         gamesnd_play_iface(SND_USER_SELECT);
1107                         break;
1108
1109                 case RESET_BUTTON:
1110                         reset_hotkeys();
1111                         gamesnd_play_iface(SND_USER_SELECT);
1112                         break;
1113         }
1114 }
1115
1116 // ---------------------------------------------------------------------
1117 // mission_hotkey_init()
1118 //
1119 // Initialize the hotkey assignment screen system.  Called when GS_STATE_HOTKEY_SCREEN
1120 // is entered.
1121 //
1122 void mission_hotkey_init()
1123 {
1124         int i;
1125         hotkey_buttons *b;
1126
1127         // pause all beam weapon sounds
1128         beam_pause_sounds();
1129
1130         // pause all game music
1131         audiostream_pause_all();
1132
1133         reset_hotkeys();
1134         common_set_interface_palette();  // set the interface palette
1135         Ui_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0);
1136         Ui_window.set_mask_bmap(Hotkey_mask_fname[gr_screen.res]);
1137
1138         for (i=0; i<NUM_BUTTONS; i++) {
1139                 b = &Buttons[gr_screen.res][i];
1140
1141                 b->button.create(&Ui_window, "", b->x, b->y, 60, 30, i < 2 ? 1 : 0, 1);
1142                 // set up callback for when a mouse first goes over a button
1143                 b->button.set_highlight_action(common_play_highlight_sound);
1144                 b->button.set_bmaps(b->filename);
1145                 b->button.link_hotspot(b->hotspot);
1146         }
1147
1148 #ifndef MAKE_FS1
1149         // add all xstr text
1150         for(i=0; i<HOTKEY_NUM_TEXT; i++) {
1151                 Ui_window.add_XSTR(&Hotkey_text[gr_screen.res][i]);
1152         }
1153 #endif
1154
1155         for (i=0; i<LIST_BUTTONS_MAX; i++) {
1156                 List_buttons[i].create(&Ui_window, "", 0, 0, 60, 30, (i < 2), 1);
1157                 List_buttons[i].hide();
1158                 List_buttons[i].disable();
1159         }
1160
1161         // set up hotkeys for buttons so we draw the correct animation frame when a key is pressed
1162         Buttons[gr_screen.res][SCROLL_UP_BUTTON].button.set_hotkey(SDLK_PAGEUP);
1163         Buttons[gr_screen.res][SCROLL_DOWN_BUTTON].button.set_hotkey(SDLK_PAGEDOWN);
1164
1165         // ensure help overlay is off
1166         help_overlay_set_state(HOTKEY_OVERLAY,0);
1167
1168         // load in relevant bitmaps
1169         Background_bitmap = bm_load(Hotkey_background_fname[gr_screen.res]);
1170         if (Background_bitmap < 0) {
1171                 // bitmap didnt load -- this is bad
1172                 Int3();
1173         }
1174         Wing_bmp = bm_load("WingDesignator");
1175         if (Wing_bmp < 0) {
1176                 // bitmap didnt load -- this is bad
1177                 Int3();
1178         }
1179
1180         Scroll_offset = 0;
1181         Selected_line = 1;
1182         hotkey_build_listing();
1183 }
1184
1185 // ---------------------------------------------------------------------
1186 // mission_hotkey_close()
1187 //
1188 // Cleanup the hotkey assignment screen system.  Called when GS_STATE_HOTKEY_SCREEN
1189 // is left.
1190 //
1191 void mission_hotkey_close()
1192 {
1193         if (Background_bitmap)
1194                 bm_unload(Background_bitmap);
1195         if (Wing_bmp >= 0)
1196                 bm_unload(Wing_bmp);
1197
1198         // unload the overlay bitmap
1199 #ifdef MAKE_FS1
1200         help_overlay_unload(HOTKEY_OVERLAY);
1201 #endif
1202
1203         // unpause all beam weapon sounds
1204         beam_unpause_sounds();
1205
1206         // unpause all game music
1207         audiostream_unpause_all();
1208
1209         Ui_window.destroy();
1210         common_free_interface_palette();                // restore game palette
1211         game_flush();
1212 }
1213
1214 // ---------------------------------------------------------------------
1215 // mission_hotkey_do_frame()
1216 //
1217 // Called once per frame to process user input for the Hotkey Assignment Screen
1218 //
1219 void mission_hotkey_do_frame(float frametime)
1220 {
1221         char buf[256];
1222         int i, k, w, h, y, z, line, hotkeys;
1223         int font_height = gr_get_font_height();
1224         int select_tease_line = -1;  // line mouse is down on, but won't be selected until button released
1225         color circle_color;
1226
1227         if ( help_overlay_active(HOTKEY_OVERLAY) ) {
1228                 Buttons[gr_screen.res][HELP_BUTTON].button.reset_status();
1229                 Ui_window.set_ignore_gadgets(1);
1230         }
1231
1232         k = Ui_window.process() & ~KEY_DEBUGGED;
1233
1234         if ( (k > 0) || B1_JUST_RELEASED ) {
1235                 if ( help_overlay_active(HOTKEY_OVERLAY) ) {
1236                         help_overlay_set_state(HOTKEY_OVERLAY, 0);
1237                         Ui_window.set_ignore_gadgets(0);
1238                         k = 0;
1239                 }
1240         }
1241
1242         if ( !help_overlay_active(HOTKEY_OVERLAY) ) {
1243                 Ui_window.set_ignore_gadgets(0);
1244         }
1245
1246         switch (k) {
1247                 case SDLK_DOWN:  // scroll list down
1248                         hotkey_scroll_line_down();
1249                         break;
1250
1251                 case SDLK_UP:  // scroll list up
1252                         hotkey_scroll_line_up();
1253                         break;
1254
1255                 case SDLK_PAGEDOWN:  // scroll list down
1256                         hotkey_scroll_screen_down();
1257                         break;
1258
1259                 case SDLK_PAGEUP:  // scroll list up
1260                         hotkey_scroll_screen_up();
1261                         break;
1262
1263                 case KEY_CTRLED | SDLK_RETURN:
1264                         save_hotkeys();
1265                         // fall through to next state -- allender changed this behavior since ESC should always cancel, no?
1266
1267                 case SDLK_ESCAPE:
1268                         mission_hotkey_exit();
1269                         break;
1270
1271                 case SDLK_TAB:
1272                 case SDLK_RETURN:
1273                 case SDLK_KP_ENTER:
1274                         expand_wing();
1275                         break;
1276
1277                 case SDLK_EQUALS:
1278                 case SDLK_KP_PLUS:
1279                         add_hotkey(Cur_hotkey);
1280                         break;
1281
1282                 case SDLK_MINUS:
1283                 case SDLK_KP_MINUS:
1284                         remove_hotkey();
1285                         break;
1286
1287                 case SDLK_F2:
1288                         gameseq_post_event(GS_EVENT_OPTIONS_MENU);                      
1289                         break;
1290
1291                 case KEY_CTRLED | SDLK_r:
1292                         reset_hotkeys();
1293                         break;
1294
1295                 case KEY_CTRLED | SDLK_c:
1296                         clear_hotkeys();
1297                         break;
1298         }       // end switch
1299
1300         // ?
1301         for (i=0; i<MAX_KEYED_TARGETS; i++) {
1302                 if (k == Key_sets[i])
1303                         Cur_hotkey = i;
1304
1305                 if (k == (Key_sets[i] | KEY_SHIFTED))
1306                         add_hotkey(i);
1307         }
1308
1309         // handle pressed buttons
1310         for (i=0; i<NUM_BUTTONS; i++) {
1311                 if (Buttons[gr_screen.res][i].button.pressed()) {
1312                         hotkey_button_pressed(i);
1313                         break;                                  // only need to handle 1 button @ a time
1314                 }
1315         }
1316
1317         for (i=0; i<LIST_BUTTONS_MAX; i++) {
1318                 // check for tease line
1319                 if (List_buttons[i].button_down()) {
1320                         select_tease_line = i + Scroll_offset;
1321                 }
1322         
1323                 // check for selected list item
1324                 if (List_buttons[i].pressed()) {
1325                         Selected_line = i + Scroll_offset;
1326                         List_buttons[i].get_mouse_pos(&z, NULL);
1327                         z += Hotkey_list_coords[gr_screen.res][0];              // adjust to full screen space
1328                         if ((z >= Hotkey_wing_icon_x[gr_screen.res]) && (z < (Hotkey_wing_icon_x[gr_screen.res]) + Hotkey_function_field_width[gr_screen.res])) {
1329                                 expand_wing();
1330                         }
1331                 }
1332
1333                 if (List_buttons[i].double_clicked()) {
1334                         Selected_line = i + Scroll_offset;
1335                         hotkeys = -1;
1336                         switch (Hotkey_lines[Selected_line].type) {
1337                                 case HOTKEY_LINE_WING:
1338                                         hotkeys = get_wing_hotkeys(Hotkey_lines[Selected_line].index);
1339                                         break;
1340
1341                                 case HOTKEY_LINE_SHIP:
1342                                 case HOTKEY_LINE_SUBSHIP:
1343                                         hotkeys = Hotkey_bits[Hotkey_lines[Selected_line].index];
1344                                         break;
1345                         }
1346
1347                         if (hotkeys != -1) {
1348                                 if (hotkeys & (1 << Cur_hotkey))
1349                                         remove_hotkey();
1350                                 else
1351                                         add_hotkey(Cur_hotkey);
1352                         }
1353                 }
1354         }
1355
1356         if (Background_bitmap >= 0) {
1357                 gr_set_bitmap(Background_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1358                 gr_bitmap(0, 0);
1359
1360         } else
1361                 gr_clear();
1362
1363         Ui_window.draw();
1364         gr_init_color(&circle_color, 160, 160, 0);
1365
1366         // draw the big "F10" in the little box 
1367         gr_set_font(FONT2);
1368         gr_set_color_fast(&Color_text_normal);
1369         SDL_strlcpy(buf, Scan_code_text[SDL_GetScancodeFromKey(Key_sets[Cur_hotkey])], SDL_arraysize(buf));
1370         gr_get_string_size(&w, &h, buf);
1371         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);
1372
1373         gr_set_font(FONT1);
1374         line = Scroll_offset;
1375         while (hotkey_line_query_visible(line)) {
1376                 z = Hotkey_lines[line].index;
1377                 y = Hotkey_list_coords[gr_screen.res][1] + Hotkey_lines[line].y - Hotkey_lines[Scroll_offset].y;
1378                 hotkeys = 0;
1379                 switch (Hotkey_lines[line].type) {
1380                         case HOTKEY_LINE_HEADING:
1381                                 gr_set_color_fast(&Color_text_heading);
1382
1383                                 gr_get_string_size(&w, &h, Hotkey_lines[line].label);
1384                                 i = y + h / 2 - 1;
1385                                 gr_line(Hotkey_list_coords[gr_screen.res][0], i, Hotkey_ship_x[gr_screen.res] - 2, i);
1386                                 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);
1387                                 break;
1388
1389                         case HOTKEY_LINE_WING:
1390                                 gr_set_bitmap(Wing_bmp, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1391                                 bm_get_info(Wing_bmp, NULL, &h, NULL);
1392                                 i = y + font_height / 2 - h / 2 - 1;
1393                                 gr_bitmap(Hotkey_wing_icon_x[gr_screen.res], i);
1394
1395 //                              i = y + font_height / 2 - 1;
1396 //                              gr_set_color_fast(&circle_color);
1397 //                              gr_circle(ICON_LIST_X + 4, i, 5);
1398
1399 //                              gr_set_color_fast(&Color_bright);
1400 //                              gr_line(ICON_LIST_X, i, ICON_LIST_X + 2, i);
1401 //                              gr_line(ICON_LIST_X + 4, i - 4, ICON_LIST_X + 4, i - 2);
1402 //                              gr_line(ICON_LIST_X + 6, i, ICON_LIST_X + 8, i);
1403 //                              gr_line(ICON_LIST_X + 4, i + 2, ICON_LIST_X + 4, i + 4);
1404
1405                                 hotkeys = get_wing_hotkeys(Hotkey_lines[line].index);
1406                                 break;
1407
1408                         case HOTKEY_LINE_SHIP:
1409                         case HOTKEY_LINE_SUBSHIP:
1410                                 hotkeys = Hotkey_bits[Hotkey_lines[line].index];
1411                                 break;
1412
1413                         default:
1414                                 Int3();
1415                 }
1416
1417                 if (Hotkey_lines[line].type != HOTKEY_LINE_HEADING) {
1418                         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);
1419                         List_buttons[line - Scroll_offset].enable();
1420                         if (hotkeys & (1 << Cur_hotkey)) {
1421                                 if (line == Selected_line)
1422                                         gr_set_color_fast(&Color_text_active_hi);
1423                                 else
1424                                         gr_set_color_fast(&Color_text_active);
1425
1426                         } else {
1427                                 if (line == Selected_line)
1428                                         gr_set_color_fast(&Color_text_selected);
1429                                 else if (line == select_tease_line)
1430                                         gr_set_color_fast(&Color_text_subselected);
1431                                 else
1432                                         gr_set_color_fast(&Color_text_normal);
1433                         }
1434
1435                 } else {
1436                         List_buttons[line - Scroll_offset].disable();
1437                 }
1438
1439                 // print active hotkeys associated for this line
1440                 if (hotkeys) {
1441                         for (i=0; i<MAX_KEYED_TARGETS; i++) {
1442                                 if (hotkeys & (1 << i)) {
1443                                         gr_printf(Hotkey_list_coords[gr_screen.res][0] + Hotkey_function_field_width[gr_screen.res]*i, y, Scan_code_text[SDL_GetScancodeFromKey(Key_sets[i])]);
1444                                 }
1445                         }
1446 /*
1447                         *buf = 0;
1448                         for (i=0; i<MAX_KEYED_TARGETS; i++) {
1449                                 if (hotkeys & (1 << i)) {
1450                                         strcat(buf, Scan_code_text[Key_sets[i]]);
1451                                         strcat(buf, ", ");
1452                                 }
1453                         }
1454
1455                         SDL_assert(strlen(buf) > 1);
1456                         buf[strlen(buf) - 2] = 0;  // lose the ", " on the end
1457
1458                         gr_force_fit_string(buf, 255, GROUP_LIST_W);
1459                         gr_printf(GROUP_LIST_X, y, buf);*/
1460                 }
1461         
1462                 // draw ship/wing name
1463                 SDL_strlcpy(buf, Hotkey_lines[line].label, SDL_arraysize(buf));
1464                 if (Hotkey_lines[line].type == HOTKEY_LINE_SUBSHIP) {
1465                         // indent
1466                         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));
1467                         gr_printf(Hotkey_ship_x[gr_screen.res]+20, y, buf);
1468                 } else {
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]);
1470                         gr_printf(Hotkey_ship_x[gr_screen.res], y, buf);
1471                 }
1472
1473                 line++;
1474         }
1475
1476         i = line - Scroll_offset;
1477         while (i < LIST_BUTTONS_MAX)
1478                 List_buttons[i++].disable();
1479
1480         // blit help overlay if active
1481         help_overlay_maybe_blit(HOTKEY_OVERLAY);
1482
1483         gr_flip();
1484 }
1485
1486 void mission_hotkey_exit()
1487 {
1488         gameseq_post_event(GS_EVENT_PREVIOUS_STATE);
1489 }