2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
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
10 * $Logfile: /Freespace2/code/Mission/MissionHotKey.cpp $
15 * C module for the Hotkey selection screen
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
25 * Revision 1.6 2004/09/20 01:31:44 theoddone33
28 * Revision 1.5 2003/05/25 02:30:43 taylor
31 * Revision 1.4 2002/06/09 04:41:22 relnev
32 * added copyright header
34 * Revision 1.3 2002/06/01 07:12:33 relnev
35 * a few NDEBUG updates.
37 * removed a few warnings.
39 * Revision 1.2 2002/05/07 03:16:46 theoddone33
40 * The Great Newline Fix
42 * Revision 1.1.1.1 2002/05/03 03:28:10 root
46 * 5 10/14/99 2:50p Jefff
49 * 4 8/17/99 3:00p Jefff
52 * 3 10/13/98 9:28a Dave
53 * Started neatening up freespace.h. Many variables renamed and
54 * reorganized. Added AlphaColors.[h,cpp]
56 * 2 10/07/98 10:53a Dave
59 * 1 10/07/98 10:49a Dave
61 * 54 6/09/98 5:15p Lawrance
62 * French/German localization
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
70 * 52 5/26/98 11:10a Lawrance
71 * Fix bug where window controls get disabled when F1 pressed twice
73 * 51 5/06/98 10:47a Allender
74 * allow escape pods to be shown on hotkey screen
76 * 50 5/05/98 1:49a Lawrance
77 * Add in missing help overlays
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
84 * 48 4/20/98 12:36a Mike
85 * Make team vs. team work when player is hostile. Several targeting
88 * 47 4/13/98 10:52a Jasen
89 * Updated coords for new Hotkey config screen.
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.
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).
99 * 44 3/10/98 9:51a Allender
100 * minor fixups to hotkey stuff.
102 * 43 3/07/98 8:09p Allender
103 * maybe restore hotkeys when setting defaults
105 * 42 2/27/98 4:31p Allender
106 * don't show ships on hotkey screen which are hidden to sensors
108 * 41 2/23/98 8:14a John
110 * 40 2/23/98 8:06a John
111 * Externalized some strings
113 * 39 2/22/98 12:19p John
114 * Externalized some strings
116 * 38 2/10/98 2:06p Hoffoss
117 * Eliminated cargo (and NavBouys) from hotkey list.
119 * 37 1/29/98 10:26a Hoffoss
120 * Made changes so arrow buttons repeat scrolling when held down.
122 * 36 1/28/98 6:22p Dave
123 * Made standalone use ~8 megs less memory. Fixed multiplayer submenu
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).
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.
135 * 33 1/18/98 5:09p Lawrance
136 * Added support for TEAM_TRAITOR
138 * 32 1/14/98 5:22p Allender
139 * save/restore hotkey selections when replaying the same mission
141 * 31 12/15/97 12:13p Hoffoss
142 * Changed code to allow hotkey listing to repeat scroll when mouse held
145 * 30 12/10/97 2:30p Hoffoss
146 * Removed dead code that isn't being used anymore.
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
152 * 28 12/03/97 4:16p Hoffoss
153 * Changed sound stuff used in interface screens for interface purposes.
155 * 27 12/01/97 3:39p Hoffoss
156 * Changed naming of headings.
158 * 26 12/01/97 3:29p Jasen
159 * Fixed button coordinates.
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.
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
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
173 * 22 11/10/97 5:36p Hoffoss
174 * Fixed bug in last fix. :)
179 #include "missionhotkey.h"
180 #include "gamesequence.h"
181 #include "freespace.h"
187 #include "audiostr.h"
190 #include "linklist.h"
191 #include "hudtarget.h"
195 #include "missionscreencommon.h"
198 #include "controlsconfig.h"
199 #include "contexthelp.h"
200 #include "alphacolors.h"
203 static int Key_sets[MAX_KEYED_TARGETS] = {
214 /////////////////////////////
216 static int Hotkey_bits[MAX_SHIPS]; // bitfield indicating which hotkeys are used by each ship
218 static int Hotkey_sets_saved; // have we saved the sets for this mission
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
225 char name[NAME_LENGTH];
228 HK_save_info Hotkey_saved_info[MAX_HOTKEY_TARGET_ITEMS];
229 int Num_hotkeys_saved;
232 static const char *Hotkey_background_fname[GR_NUM_RESOLUTIONS] = {
234 "2_Hotkeys" // GR_1024
237 static const char *Hotkey_mask_fname[GR_NUM_RESOLUTIONS] = {
238 "Hotkeys-M", // GR_640
239 "2_Hotkeys-M" // GR_1024
242 //#define GROUP_LIST_X 40
243 //#define GROUP_LIST_W 160
245 // #define ICON_LIST_X 219
246 // #define ICON_LIST_W 8
248 // #define ICON_LIST_X 280
249 // #define ICON_LIST_W 8
251 //#define SHIP_LIST_X 242
252 //#define SHIP_LIST_X2 259
253 //#define SHIP_LIST_W 341
254 //#define SHIP_LIST_W2 324
256 // #define SHIP_LIST_X 302
257 // #define SHIP_LIST_X2 319
258 // #define SHIP_LIST_W 281
259 // #define SHIP_LIST_W2 264
262 // #define LIST_H 280
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
274 #define WING_FLAG 0x80000
276 #define MAX_LINES 200
277 #define NUM_BUTTONS 10
278 #define LIST_BUTTONS_MAX 40
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
291 // coords for entire ship box
292 static int Hotkey_list_coords[GR_NUM_RESOLUTIONS][4] = {
313 // coords for big "F9" thing in the corner
314 static int Hotkey_function_name_coords[GR_NUM_RESOLUTIONS][4] = {
336 #define FIELD_LEFT_EDGE 0
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] = {
352 29, 56, 83, 110, 137, 164, 191, 218, 245, 280, 531
355 47, 91, 135, 179, 223, 267, 311, 355, 399, 448, 849
360 static int Hotkey_function_field_width[GR_NUM_RESOLUTIONS] = {
364 static int Hotkey_wing_icon_x[GR_NUM_RESOLUTIONS] = {
368 static int Hotkey_ship_x[GR_NUM_RESOLUTIONS] = {
373 struct hotkey_buttons {
374 const char *filename;
377 UI_BUTTON button; // because we have a class inside this struct, we need the constructor below..
379 hotkey_buttons(const char *name, int x1, int y1, int h) : filename(name), x(x1), y(y1), hotspot(h) {}
382 // button definitions
383 static hotkey_buttons Buttons[GR_NUM_RESOLUTIONS][NUM_BUTTONS] = {
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
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)
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)
428 #define HOTKEY_NUM_TEXT 6
430 static UI_XSTR Hotkey_text[GR_NUM_RESOLUTIONS][HOTKEY_NUM_TEXT] = {
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 }
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 }
458 int y; // Y coordinate of line
459 } Hotkey_lines[MAX_LINES];
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;
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;
471 //////////////////////
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 )
480 for (i = 0; i < MAX_KEYED_TARGETS; i++ ) {
481 if ( Key_sets[i] == k ) {
486 Int3(); // get allender
490 // function to maybe restore some hotkeys during the first N seconds of the mission
491 void mission_hotkey_maybe_restore()
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 )
500 // the ship is present, add it to the given set.
501 index = ship_name_lookup(Hotkey_saved_info[i].name);
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;
509 // ---------------------------------------------------------------------
510 // mission_hotkey_set_defaults()
512 // Set up the hotkey lists for the player based on the mission designer
515 void mission_hotkey_set_defaults()
522 for ( i = 0; i < MAX_KEYED_TARGETS; i++ ) {
523 hud_target_hotkey_clear(i);
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);
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();
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) ) {
542 if ( (A == &obj_used_list) || (A->type != OBJ_SHIP) || ((Game_mode & GM_NORMAL) && (A == Player_obj)) ) {
546 SDL_assert(A->instance >= 0 && A->instance < MAX_SHIPS);
547 sp = &Ships[A->instance];
549 if ( sp->hotkey == -1 )
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
555 if ( sp->hotkey == MAX_KEYED_TARGETS )
558 SDL_assert(sp->objnum >= 0);
559 hud_target_hotkey_add_remove( sp->hotkey, &Objects[sp->objnum], HOTKEY_MISSION_FILE_ADDED );
562 // Check for wings with a hotkey assigned
563 for ( i = 0; i < num_wings; i++ ) {
566 if ( wp->hotkey == -1 )
569 // like ships, skip this wing if the hotkey is the last hotkey item
570 if ( wp->hotkey == MAX_KEYED_TARGETS )
573 for ( j = 0; j < wp->current_count; j++ ) {
574 if ( wp->ship_index[j] == -1 )
577 sp = &Ships[wp->ship_index[j]];
578 hud_target_hotkey_add_remove( wp->hotkey, &Objects[sp->objnum], HOTKEY_MISSION_FILE_ADDED );
583 // function to reset the saved hotkeys -- called when a new mission is loaded
584 void mission_hotkey_reset_saved()
586 Num_hotkeys_saved = 0;
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()
594 htarget_list *hitem, *plist;
597 if ( !timestamp_elapsed(Mission_hotkey_save_timestamp) ) {
598 mission_hotkey_maybe_restore();
602 // no processing if we have saved them.
603 if ( Hotkey_sets_saved )
606 for ( i = 0; i < MAX_HOTKEY_TARGET_ITEMS; i++ )
607 Hotkey_saved_info[i].setnum = -1;
609 Num_hotkeys_saved = 0;
610 hkp = &(Hotkey_saved_info[0]);
612 for ( i = 0; i < MAX_KEYED_TARGETS; i++ ) {
614 // get the list. do nothing if list is empty
615 plist = &(Player->keyed_targets[i]);
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 );
622 SDL_strlcpy( hkp->name, Ships[hitem->objp->instance].ship_name, sizeof(hkp->name) );
628 Hotkey_sets_saved = 1;
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 )
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) )
640 // we can add it to the set
641 hud_target_hotkey_add_remove( set, &Objects[objnum], how_to_add );
644 void mission_hotkey_validate()
646 htarget_list *hitem, *plist;
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
655 hitem = GET_FIRST(plist);
656 while ( hitem != END_OF_LIST(plist) ) {
658 // ensure this object is still valid and in the obj_used_list
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 ) {
666 if ( obj_valid == FALSE ) {
669 temp = GET_NEXT(hitem);
670 list_remove( plist, hitem );
671 list_append( &htarget_free_list, hitem );
676 hitem = GET_NEXT( hitem );
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)
685 int i, total = 0xffffffff;
687 SDL_assert((n >= 0) && (n < num_wings));
688 for (i=0; i<Wings[n].current_count; i++) {
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 )
697 total &= Hotkey_bits[Wings[n].ship_index[i]];
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)
706 if (Num_lines >= MAX_LINES)
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;
716 // insert a line of hotkey smuck before line 'n'.
717 int hotkey_line_insert(int n, const char *text, int type, int index)
721 if (Num_lines >= MAX_LINES)
726 Hotkey_lines[z] = Hotkey_lines[z - 1];
730 Hotkey_lines[z].label = text;
731 Hotkey_lines[z].type = type;
732 Hotkey_lines[z].index = index;
736 // insert a line of hotkey smuck somewhere between 'start' and end of list such that it is
738 int hotkey_line_add_sorted(const char *text, int type, int index, int start)
742 if (Num_lines >= MAX_LINES)
746 while ((z >= start) && ((Hotkey_lines[z].type == HOTKEY_LINE_SUBSHIP) || (SDL_strcasecmp(text, Hotkey_lines[z].label) < 0)))
750 while ((z < Num_lines) && (Hotkey_lines[z].type == HOTKEY_LINE_SUBSHIP))
753 return hotkey_line_insert(z, text, type, index);
756 int hotkey_get_team(int i)
758 if (Ships[i].team == Player_ship->team)
759 return TEAM_FRIENDLY;
764 int hotkey_build_team_listing(int team, int y)
767 const char *str = NULL;
768 int i, j, s, z, start;
769 int font_height = gr_get_font_height();
771 for (i=0; i<MAX_SHIPS; i++)
772 if (hotkey_get_team(i) == team)
778 if (team == Player_ship->team)
779 str = XSTR( "Friendly ships", 402);
781 str = XSTR( "Enemy ships", 403);
784 hotkey_line_add(str, HOTKEY_LINE_HEADING, 0, y);
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);
797 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
800 // don't process non-ships, or the player ship
801 if ( (Game_mode & GM_NORMAL) && (so->objnum == OBJ_INDEX(Player_obj)) )
804 shipnum = Objects[so->objnum].instance;
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) )
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)
814 // don't process ships invisible to sensors, dying or departing
815 if ( Ships[shipnum].flags & (SF_HIDDEN_FROM_SENSORS|SF_DYING|SF_DEPARTING) )
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;
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);
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)) {
835 // special check for the player's wing. If he's in a wing, and the only guy left, don't
837 if ( (Player_ship->wingnum == i) && (Wings[i].current_count == 1) )
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;
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 )
853 // if we didn't reach the end of the list, don't display the wing
854 if ( j < Wings[i].current_count )
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);
867 z = HOTKEY_LINE_HEADING;
868 for (i=start; i<Num_lines; i++) {
869 if (Hotkey_lines[i].type == HOTKEY_LINE_SUBSHIP)
872 y += font_height + 2;
874 Hotkey_lines[i].y = y;
877 y += font_height + 8;
881 void hotkey_build_listing()
887 y = hotkey_build_team_listing(TEAM_FRIENDLY, y);
888 y = hotkey_build_team_listing(TEAM_HOSTILE, y);
891 int hotkey_line_query_visible(int n)
895 if ((n < 0) || (n >= Num_lines))
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]))
905 void hotkey_scroll_screen_up()
909 SDL_assert(Selected_line > Scroll_offset);
910 while (!hotkey_line_query_visible(Selected_line) || (Hotkey_lines[Selected_line].type == HOTKEY_LINE_HEADING))
913 gamesnd_play_iface(SND_SCROLL);
916 gamesnd_play_iface(SND_GENERAL_FAIL);
919 void hotkey_scroll_line_up()
921 if (Selected_line > 1) {
923 while (Hotkey_lines[Selected_line].type == HOTKEY_LINE_HEADING)
926 if (Selected_line < Scroll_offset)
927 Scroll_offset = Selected_line;
929 gamesnd_play_iface(SND_SCROLL);
932 gamesnd_play_iface(SND_GENERAL_FAIL);
935 void hotkey_scroll_screen_down()
937 if (Hotkey_lines[Num_lines - 1].y + gr_get_font_height() > Hotkey_lines[Scroll_offset].y + Hotkey_list_coords[gr_screen.res][3]) {
939 while (!hotkey_line_query_visible(Selected_line) || (Hotkey_lines[Selected_line].type == HOTKEY_LINE_HEADING)) {
941 SDL_assert(Selected_line < Num_lines);
944 gamesnd_play_iface(SND_SCROLL);
947 gamesnd_play_iface(SND_GENERAL_FAIL);
950 void hotkey_scroll_line_down()
952 if (Selected_line < Num_lines - 1) {
954 while (Hotkey_lines[Selected_line].type == HOTKEY_LINE_HEADING)
957 SDL_assert(Selected_line > Scroll_offset);
958 while (!hotkey_line_query_visible(Selected_line))
961 gamesnd_play_iface(SND_SCROLL);
964 gamesnd_play_iface(SND_GENERAL_FAIL);
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)) {
986 htarget_list *hitem, *plist;
988 for (i=0; i<MAX_SHIPS; i++)
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
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);
1003 void clear_hotkeys()
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;
1014 } else if ((z == HOTKEY_LINE_SHIP) || (z == HOTKEY_LINE_SUBSHIP)) {
1015 Hotkey_bits[Hotkey_lines[Selected_line].index] = 0;
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 );
1034 void add_hotkey(int hotkey)
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);
1044 } else if ((z == HOTKEY_LINE_SHIP) || (z == HOTKEY_LINE_SUBSHIP)) {
1045 Hotkey_bits[Hotkey_lines[Selected_line].index] |= (1 << hotkey);
1049 void remove_hotkey()
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);
1059 } else if ((z == HOTKEY_LINE_SHIP) || (z == HOTKEY_LINE_SUBSHIP)) {
1060 Hotkey_bits[Hotkey_lines[Selected_line].index] &= ~(1 << Cur_hotkey);
1064 void hotkey_button_pressed(int n)
1067 case SCROLL_UP_BUTTON:
1068 hotkey_scroll_screen_up();
1071 case SCROLL_DOWN_BUTTON:
1072 hotkey_scroll_screen_down();
1075 case ADD_HOTKEY_BUTTON:
1076 add_hotkey(Cur_hotkey);
1077 gamesnd_play_iface(SND_USER_SELECT);
1080 case REMOVE_HOTKEY_BUTTON:
1082 gamesnd_play_iface(SND_USER_SELECT);
1087 // fall through to CANCEL_BUTTON
1090 mission_hotkey_exit();
1091 gamesnd_play_iface(SND_USER_SELECT);
1095 launch_context_help();
1096 gamesnd_play_iface(SND_HELP_PRESSED);
1099 case OPTIONS_BUTTON:
1100 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
1101 gamesnd_play_iface(SND_USER_SELECT);
1106 gamesnd_play_iface(SND_USER_SELECT);
1111 gamesnd_play_iface(SND_USER_SELECT);
1116 // ---------------------------------------------------------------------
1117 // mission_hotkey_init()
1119 // Initialize the hotkey assignment screen system. Called when GS_STATE_HOTKEY_SCREEN
1122 void mission_hotkey_init()
1127 // pause all beam weapon sounds
1128 beam_pause_sounds();
1130 // pause all game music
1131 audiostream_pause_all();
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]);
1138 for (i=0; i<NUM_BUTTONS; i++) {
1139 b = &Buttons[gr_screen.res][i];
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);
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]);
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();
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);
1165 // ensure help overlay is off
1166 help_overlay_set_state(HOTKEY_OVERLAY,0);
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
1174 Wing_bmp = bm_load("WingDesignator");
1176 // bitmap didnt load -- this is bad
1182 hotkey_build_listing();
1185 // ---------------------------------------------------------------------
1186 // mission_hotkey_close()
1188 // Cleanup the hotkey assignment screen system. Called when GS_STATE_HOTKEY_SCREEN
1191 void mission_hotkey_close()
1193 if (Background_bitmap)
1194 bm_unload(Background_bitmap);
1196 bm_unload(Wing_bmp);
1198 // unload the overlay bitmap
1200 help_overlay_unload(HOTKEY_OVERLAY);
1203 // unpause all beam weapon sounds
1204 beam_unpause_sounds();
1206 // unpause all game music
1207 audiostream_unpause_all();
1209 Ui_window.destroy();
1210 common_free_interface_palette(); // restore game palette
1214 // ---------------------------------------------------------------------
1215 // mission_hotkey_do_frame()
1217 // Called once per frame to process user input for the Hotkey Assignment Screen
1219 void mission_hotkey_do_frame(float frametime)
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
1227 if ( help_overlay_active(HOTKEY_OVERLAY) ) {
1228 Buttons[gr_screen.res][HELP_BUTTON].button.reset_status();
1229 Ui_window.set_ignore_gadgets(1);
1232 k = Ui_window.process() & ~KEY_DEBUGGED;
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);
1242 if ( !help_overlay_active(HOTKEY_OVERLAY) ) {
1243 Ui_window.set_ignore_gadgets(0);
1247 case SDLK_DOWN: // scroll list down
1248 hotkey_scroll_line_down();
1251 case SDLK_UP: // scroll list up
1252 hotkey_scroll_line_up();
1255 case SDLK_PAGEDOWN: // scroll list down
1256 hotkey_scroll_screen_down();
1259 case SDLK_PAGEUP: // scroll list up
1260 hotkey_scroll_screen_up();
1263 case KEY_CTRLED | SDLK_RETURN:
1265 // fall through to next state -- allender changed this behavior since ESC should always cancel, no?
1268 mission_hotkey_exit();
1279 add_hotkey(Cur_hotkey);
1288 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
1291 case KEY_CTRLED | SDLK_r:
1295 case KEY_CTRLED | SDLK_c:
1301 for (i=0; i<MAX_KEYED_TARGETS; i++) {
1302 if (k == Key_sets[i])
1305 if (k == (Key_sets[i] | KEY_SHIFTED))
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
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;
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])) {
1333 if (List_buttons[i].double_clicked()) {
1334 Selected_line = i + Scroll_offset;
1336 switch (Hotkey_lines[Selected_line].type) {
1337 case HOTKEY_LINE_WING:
1338 hotkeys = get_wing_hotkeys(Hotkey_lines[Selected_line].index);
1341 case HOTKEY_LINE_SHIP:
1342 case HOTKEY_LINE_SUBSHIP:
1343 hotkeys = Hotkey_bits[Hotkey_lines[Selected_line].index];
1347 if (hotkeys != -1) {
1348 if (hotkeys & (1 << Cur_hotkey))
1351 add_hotkey(Cur_hotkey);
1356 if (Background_bitmap >= 0) {
1357 gr_set_bitmap(Background_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1364 gr_init_color(&circle_color, 160, 160, 0);
1366 // draw the big "F10" in the little box
1368 gr_set_color_fast(&Color_text_normal);
1369 SDL_strlcpy(buf, Scan_code_text[Key_sets[Cur_hotkey]], sizeof(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);
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;
1379 switch (Hotkey_lines[line].type) {
1380 case HOTKEY_LINE_HEADING:
1381 gr_set_color_fast(&Color_text_heading);
1383 gr_get_string_size(&w, &h, Hotkey_lines[line].label);
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);
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);
1395 // i = y + font_height / 2 - 1;
1396 // gr_set_color_fast(&circle_color);
1397 // gr_circle(ICON_LIST_X + 4, i, 5);
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);
1405 hotkeys = get_wing_hotkeys(Hotkey_lines[line].index);
1408 case HOTKEY_LINE_SHIP:
1409 case HOTKEY_LINE_SUBSHIP:
1410 hotkeys = Hotkey_bits[Hotkey_lines[line].index];
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);
1424 gr_set_color_fast(&Color_text_active);
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);
1432 gr_set_color_fast(&Color_text_normal);
1436 List_buttons[line - Scroll_offset].disable();
1439 // print active hotkeys associated for this line
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[Key_sets[i]]);
1448 for (i=0; i<MAX_KEYED_TARGETS; i++) {
1449 if (hotkeys & (1 << i)) {
1450 strcat(buf, Scan_code_text[Key_sets[i]]);
1455 SDL_assert(strlen(buf) > 1);
1456 buf[strlen(buf) - 2] = 0; // lose the ", " on the end
1458 gr_force_fit_string(buf, 255, GROUP_LIST_W);
1459 gr_printf(GROUP_LIST_X, y, buf);*/
1462 // draw ship/wing name
1463 SDL_strlcpy(buf, Hotkey_lines[line].label, sizeof(buf));
1464 if (Hotkey_lines[line].type == HOTKEY_LINE_SUBSHIP) {
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);
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);
1476 i = line - Scroll_offset;
1477 while (i < LIST_BUTTONS_MAX)
1478 List_buttons[i++].disable();
1480 // blit help overlay if active
1481 help_overlay_maybe_blit(HOTKEY_OVERLAY);
1486 void mission_hotkey_exit()
1488 gameseq_post_event(GS_EVENT_PREVIOUS_STATE);